aboutsummaryrefslogtreecommitdiffstats
path: root/src/argparser.c
diff options
context:
space:
mode:
authorMattias Andrée <maandree@operamail.com>2013-06-19 17:34:18 +0200
committerMattias Andrée <maandree@operamail.com>2013-06-19 17:34:18 +0200
commit565ee8a08e7c65b65b0c8147f3c6a23b350e2b6c (patch)
treee05196757231d796df9e35bdcaa20d5ade7ea700 /src/argparser.c
parentm (diff)
downloadargparser-565ee8a08e7c65b65b0c8147f3c6a23b350e2b6c.tar.gz
argparser-565ee8a08e7c65b65b0c8147f3c6a23b350e2b6c.tar.bz2
argparser-565ee8a08e7c65b65b0c8147f3c6a23b350e2b6c.tar.xz
beginning of c version
Signed-off-by: Mattias Andrée <maandree@operamail.com>
Diffstat (limited to '')
-rw-r--r--src/argparser.c681
1 files changed, 677 insertions, 4 deletions
diff --git a/src/argparser.c b/src/argparser.c
index a3020d6..b72639f 100644
--- a/src/argparser.c
+++ b/src/argparser.c
@@ -20,19 +20,692 @@
#include <stdio.h>
+#define true 1
+#define false 0
+#define null 0
+
+#define ARGUMENTLESS 0
+#define ARGUMENTED 1
+#define VARIADIC 2
+
+
+static void sort(char** list, long count);
+static long cmp(char* a, char* b);
+
+
+/**
+ * Whether the Linux VT is being used
+ */
long args_linuxvt;
+
+/**
+ * The name of the executed command
+ */
char* args_program;
+
+/**
+ * Whether to free the member of `args_program`
+ */
+long args_program_dispose;
+
+/**
+ * Short, single-line, description of the program
+ */
char* args_dscription;
+
+/**
+ * Formated, multi-line, usage text, `null` if none
+ */
char* args_usage;
+
+/**
+ * Long, multi-line, description of the program, `null` if none
+ */
char* args_longdscription;
+/**
+ * The error output stream
+ */
+FILE* args_out;
+
+/**
+ * The passed arguments
+ */
+char** args_arguments;
-extern void args_init(char* dscription, char* usage, char* longdscription, char* program, long usestderr)
+/**
+ * The number of passed arguments
+ */
+long args_arguments_count;
+
+/**
+ * The number of unrecognised arguments
+ */
+long args_unrecognised_count;
+
+/**
+ * The concatination of `files` with blankspaces as delimiters, `null` if no files
+ */
+char* args_message;
+
+/**
+ * The arguments passed that is not tied to an option
+ */
+char** args_files;
+
+/**
+ * The number of elements in `args_files`
+ */
+long args_files_count;
+
+// Options, in order
+// ArrayList<Option> options = new ArrayList<Option>();
+
+// Option map
+// HashMap<String, Option> optmap = new HashMap<String, Option>();
+
+// Parsed arguments, a map from option to arguments, null` if not used, add one `null` element per argumentless use.
+// HashMap<String, String[]> opts = new HashMap<String, String[]>();
+
+
+
+/**
+ * Constructor.
+ * The short description is printed on same line as the program name
+ *
+ * @param description Short, single-line, description of the program
+ * @param usage Formated, multi-line, usage text, may be `null`
+ * @param longdescription Long, multi-line, description of the program, may be `null`
+ * @param program The name of the program, `null` for automatic
+ * @param usestderr Whether to use stderr instead of stdout
+ */
+extern void args_init(char* description, char* usage, char* longdscription, char* program, long usestderr)
{
char* term = getenv("TERM");
+ args_linuxvt = 0;
+ if (term == null)
+ if (*(term + 0) == 'l')
+ if (*(term + 1) == 'i')
+ if (*(term + 2) == 'n')
+ if (*(term + 3) == 'u')
+ if (*(term + 4) == 'x')
+ if (*(term + 5) == 0)
+ args_linuxvt = 1;
+ args_program_dispose = program == null;
+ args_program = program == null ? args_parent_name(0) : program;
+ if (args_program == null)
+ {
+ args_program = "?";
+ args_program_dispose = false;
+ }
+ args_dscription = description;
+ args_usage = usage;
+ args_longdscription = longdescription;
+ args_out = usestderr ? stderr : stdout;
+ args_arguments_count = args_unrecognised_count = args_files_count = 0;
+ args_files = args_arguments = null;
+ args_message = null;
+}
+
-args_dscription;
-args_usage;
-args_longdscription;
+/**
+ * Disposes of all resources, run this when you are done
+ */
+extern void args_dispose()
+{
+ if (args_files != null)
+ free(args_files);
+ if (args_message != null)
+ free(args_message);
+ if (args_program_dispose)
+ free(args_program);
+}
+
+
+/**
+ * Gets the name of the parent process
+ *
+ * @param levels The number of parents to walk, 0 for self, and 1 for direct parent
+ * @return The name of the parent process, `null` if not found
+ */
+extern char* args_parent_name(long levels)
+{
+ char pid[22]; /* 6 should be enough, but we want to be future proof */
+ ssize_t pid_n = readlink("/proc/self", pid, 21);
+ long lvl = levels, i, j, cmdsize;
+ size_t n;
+ FILE* is;
+ char* buf[35];
+ char* cmd;
+ char* data;
+ if (pid_n <= 0)
+ return null;
+ pid[pid_n] = 0;
+ data = (char*)malloc(2048 * sizeof(char));
+ while (lvl > 0)
+ {
+ i = 0;
+ for (j = 0; *("/proc/" + j); j++) *(buf + i++) = *("/proc/" + j);
+ for (j = 0; *(pid + j); j++) *(buf + i++) = *(pid + j);
+ for (j = 0; *("/status" + j); j++) *(buf + i++) = *("/status" + j);
+ *(buf + i++) = 0;
+ if ((is = fopen(buf, "r")) == null)
+ {
+ free(data);
+ return null;
+ }
+ n = fread(data, 1, 2048, is);
+ j = 0;
+ for (i = 0; i < n; i++)
+ {
+ char c = *(data + i);
+ if (c == '\n')
+ {
+ if (j > 5)
+ if (*(buf + 0) == 'P')
+ if (*(buf + 1) == 'P')
+ if (*(buf + 2) == 'i')
+ if (*(buf + 3) == 'd')
+ if (*(buf + 4) == ':')
+ {
+ i = 5;
+ while ((*(buf + i) == '\t') || (*(buf + i) == ' '))
+ i++;
+ j -= n = i;
+ buf += n;
+ for (i = 0; i < j; i++)
+ *(pid + i) = *(buf + i);
+ *(pid + j) = 0;
+ buf -= n;
+ lvl--;
+ break;
+ }
+ j = 0;
+ }
+ if (j < 35)
+ *(buf + j) = c;
+ }
+ free(data);
+ return null;
+ }
+ free(data);
+ i = 0;
+ for (j = 0; *("/proc/" + j); j++) *(buf + i++) = *("/proc/" + j);
+ for (j = 0; *(pid + j); j++) *(buf + i++) = *(pid + j);
+ for (j = 0; *("/cmdline" + j); j++) *(buf + i++) = *("/cmdline" + j);
+ *(buf + i++) = 0;
+ if ((is = fopen(buf, "r")) == null)
+ return null;
+ i = 0;
+ n = 0;
+ cmd = (char*)malloc(cmdsize = 128);
+ for (;;)
+ {
+ n += fread(cmd, 1, 128, is);
+ for (; i < n; i++)
+ if (*(cmd + i) == 0)
+ break;
+ if (i == n)
+ {
+ char* tmp = (char*)malloc(cmdsize + 128);
+ for (i = 0; i < n; i++)
+ *(tmp + i) = *(cmd + i);
+ cmdsize += 128;
+ free(cmd);
+ cmd = tmp;
+ }
+ else
+ break;
+ }
+ if (*cmd == 0)
+ {
+ free(cmd);
+ cmd = 0;
+ }
+ return cmd;
+}
+
+
+/**
+ * Checks the correctness of the number of used non-option arguments
+ *
+ * @param min The minimum number of files
+ * @return Whether the usage was correct
+ */
+extern long args_test_files_min(long min)
+{
+ return min <= args_files_count;
+}
+
+
+/**
+ * Checks the correctness of the number of used non-option arguments
+ *
+ * @param max The maximum number of files
+ * @return Whether the usage was correct
+ */
+extern long args_test_files_max(long max)
+{
+ return args_files_count <= max;
+}
+
+
+/**
+ * Checks the correctness of the number of used non-option arguments
+ *
+ * @param min The minimum number of files
+ * @param max The maximum number of files
+ * @return Whether the usage was correct
+ */
+extern long args_test_files(long min, long max)
+{
+ return (min <= args_files_count) && (args_files_count <= max);
+}
+
+
+/**
+ * Checks for out of context option usage
+ *
+ * @param allowed Allowed options, will be sorted
+ * @param allowed_count The number of elements in `allowed`
+ * @return Whether only allowed options was used
+ */
+extern long args_test_allowed(char** allowed, long allowed_count)
+{
+ char** opts;
+ char** a;
+ char** o;
+ long rc = true, _a, _o;
+
+ sort(allowed, _a = allowed_count);
+ opts = args_get_opts(); /* TODO */
+ sort(opts, _o = args_get_opts_count()); /* TODO */
+
+ a = allowed + _a;
+ o = opts + _o;
+
+ while (opts != o)
+ {
+ if ((allowed == a) || (cmp(*opts, *allowed) < 0))
+ if (args_opts_used(*opt)) /* TODO */
+ {
+ fprintf(args_out, "%s: option used out of context: %s", args_program, *opts);
+ char* std = args_optmap_get_std(*opt); /* TODO */
+ if (cmp(std, *opt) != 0)
+ fprintf(args_out, "(%s)", std);
+ fprintf(args_out, "\n");
+ rc = false;
+ }
+ while ((allowed != a) && (cmp(*opts, *allowed) > 0))
+ allowed++;
+ opts++;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Checks for option conflicts
+ *
+ * @param exclusives Exclusive options, will be sorted
+ * @param exclusives_count The number of elements in `exclusives`
+ * @return Whether at most one exclusive option was used
+ */
+extern long args_test_exclusiveness(char** exclusives, long exclusives_count)
+{
+ long used_ptr = 0, i = 0;
+ char** used = (char**)malloc(args_get_opts_count() * sizeof(char*));
+ char** e;
+ char** o;
+ char* std;
+ long _e, _o;
+
+ sort(exclusives, _e = exclusives_count);
+ opts = args_get_opts();
+ sort(opts, _o = args_get_opts_count());
+
+ e = exclusives + _e;
+ o = opts + _o;
+
+ while ((opts != o) && (exclusives != e))
+ {
+ while ((opts != o) && (cmp(*opts, *allowed) > 0))
+ opt++;
+ while ((allowed != a) && (cmp(*opts, *allowed) > 0))
+ allowed++;
+ if ((cmp(*opts, *allowed) == 0) && (args_opts_used(*opt)))
+ *(used + used_ptr++) = opt;
+ opts++;
+ }
+
+ if (used_ptr >= 1)
+ {
+ fprintf(args_out, "%s: conflicting options:", args_program);
+ for (; i < used_ptr; i++)
+ {
+ std = args_optmap_get_std(*(used + i));
+ if (cmp(*(used + i), std) == 0)
+ fprintf(args_out, " %s", *(used + i));
+ else
+ fprintf(args_out, " %s(%s)", *(used + i), std);
+ }
+ fprintf(args_out, "\n");
+ free(used);
+ return false;
+ }
+
+ free(used);
+ return true;
+}
+
+
+/**
+ * Maps up options that are alternatives to the first alternative for each option
+ */
+extern void args_support_alternatives()
+{
+ char** opts = args_get_optmap(); /* TODO */
+ long n = args_get_optmap_count(); /* TODO */
+ long i;
+
+ for (i = 0; i < n; i++)
+ args_opts_put(*(opts + 1), args_opts_get(args_optmap_get_std(*opt)));
+ /* TODO */ /* TODO */
+}
+
+
+/**
+ * Prints a colourful help message
+ */
+extern void args_help()
+{
+ long maxfirstlen = 0, count = 0, copts = args_get_options_count(); /* TODO */
+ char* dash = args_linuxvt ? "-" : "—";
+ char* empty;
+ char** lines;
+ long* lens;
+
+ fprintf(args_out, "\033[01m%s\033[21m %s %s\n", args_program, dash, args_description);
+ if (args_longdescription != null)
+ fprintf(args_out, "%s\n", args_longdescription);
+ fprintf(args_out, "\n");
+
+ if (args_usage != null)
+ {
+ long n = 0, lines = 0, i = 0;
+ char* buf;
+ fprintf(args_out, "\033[01mUSAGE:\033[21m\n");
+ while (*(args_usage + n))
+ if (*(args_usage + n++) == '\n')
+ lines++;
+ buf = (char*)malloc((n + 2 + lines * 7) * sizeof(char));
+ *buf++ = '\t';
+ while (i < n)
+ {
+ *buf++ = *(args_usage + i);
+ if (*(args_usage + i++) == '\n')
+ {
+ *buf++ = ' ';
+ *buf++ = ' ';
+ *buf++ = ' ';
+ *buf++ = ' ';
+ *buf++ = 'o';
+ *buf++ = 'r';
+ *buf++ = '\t';
+ }
+ }
+ *buf++ = 0;
+ buf -= n + 2 + lines * 7;
+ fprintf(args_out, "%s\n\n", buf);
+ free(buf);
+ }
+
+ {
+ long i = 0;
+ for (i = 0; i < copts; i++)
+ {
+ if (args_options_get_help(i) == null) /* TODO */
+ continue;
+ if (args_options_get_alternatives_count(i) > 1) /* TODO */
+ {
+ long n = 0;
+ char* first = *(args_options_get_alternatives(i)); /* TODO */
+ while (*(first + n))
+ n++;
+ if (maxfirstlen < n)
+ maxfirstlen = n;
+ }
+ }
+ }
+
+ empty = (char*)malloc((maxfirstlen + 1) * sizeof(char));
+ {
+ long i;
+ for (i = 0; i < maxfirstlen; i++)
+ *(empty + i++) = ' ';
+ *(empty + maxfirstlen) = 0;
+ }
+
+ fprintf(args_out, "\033[01mSYNOPSIS:\033[21m\n");
+ lines = (char**)malloc(copts * sizeof(char*));
+ lens = (long*)malloc(copts * sizeof(long));
+ {
+ char* first_extra = 0;
+ long i = 0, n, l, j, type;
+ for (i = 0; i < copts; i++)
+ {
+ char* first;
+ char* last;
+ char* line;
+ char* arg;
+ if (args_options_get_help(i) == null)
+ continue;
+ l = 0;
+ arg = *(args_options_get_argument(i)); /* TODO */
+ first = *(args_options_get_alternatives(i)); /* TODO */
+ last = *(args_options_get_alternatives(i) + args_options_get_alternatives_count(i) - 1); /* TODO */
+ type = *(args_options_get_type(i)); /* TODO */
+ if (first == last)
+ first = empty;
+ else
+ {
+ n = 0;
+ while (*(first + n))
+ n++;
+ first_extra = empty + n;
+ }
+ n = 0;
+ while (*(last + n))
+ n++;
+ if (arg != null)
+ while (*(arg + n))
+ n++;
+ l += maxfirstlen + 6 + n;
+ *(lines + count) = line = (char*)malloc((1 + 17 + 16 + maxfirstlen + n) * sizeof(char));
+ for (j = 0; *(" \033[02m" + j); j++)
+ *line++ = *(" \033[02m" + j);
+ for (j = 0; *(first + j); j++)
+ *line++ = *(first + j);
+ if (first_extra != null)
+ for (j = 0; *(first_extra + j); j++)
+ *line++ = *(first_extra + j);
+ for (j = 0; *("\033[22m \0" + j); j++)
+ *line++ = *("\033[22m \0" + j);
+ for (j = 0; *(last + j); j++)
+ *line++ = *(last + j);
+ if (type == VARIADIC)
+ {
+ for (j = 0; *(" [\033[04m" + j); j++)
+ *line++ = *(" [\033[04m" + j);
+ for (j = 0; *(arg + j); j++)
+ *line++ = *(arg + j);
+ for (j = 0; *("\033[24m...]" + j); j++)
+ *line++ = *("\033[24m...]" + j);
+ l += 6;
+ }
+ else if (type == ARGUMENTED)
+ {
+ for (j = 0; *(" \033[04m" + j); j++)
+ *line++ = *(" \033[04m" + j);
+ for (j = 0; *(arg + j); j++)
+ *line++ = *(arg + j);
+ for (j = 0; *("\033[24m" + j); j++)
+ *line++ = *("\033[24m" + j);
+ l += 1;
+ }
+ *line = 0;
+ *(lens + count++) = l;
+ }
+ }
+
+ free(empty);
+
+ {
+ long col = 0, i = 0, index = 0
+ for (; i < count; i++)
+ if (col < *(lens + i))
+ col = *(lens + i);
+ col += 8 - ((col - 4) & 7);
+
+ empty = (char*)malloc((col + 1) * sizeof(char));
+ for (i = 0; i < col; i++)
+ *(empty + i++) = ' ';
+ *(empty + col) = 0;
+ i = 0;
+
+ for (i = 0; i < copts; i++)
+ {
+ long first = true, j = 0, jptr = 0;
+ char* colour = (index & 1) == 0 ? "36" : "34";
+ char* help = args_options_get_help(i);
+ char* line;
+ char* buf;
+ char** jumps;
+ char c;
+ if (help == null)
+ continue;
+ fprintf(args_out, "%s\033[%s;01m", line = *(lines + index), colour);
+ while (*line++)
+ ;
+ fprintf(args_out, "%s%s", line, empty + *(lens + index));
+ free(*(lines + index++));
+ while ((c = *(help + j++)))
+ if (c == '\n')
+ jptr++;
+ jumps = (char**)malloc(jptr * sizeof(char*));
+ *jumps = buf = (char*)malloc(j * sizeof(char));
+ j = 0;
+ jptr = 1;
+ while ((c = *(help + j)))
+ if (c == '\n')
+ {
+ *(buf + j++) = 0;
+ *(jumps + jptr++) = buf + j;
+ }
+ else
+ *(buf + j++) = c;
+ for (j = 0; j < jptr; j++)
+ if (first)
+ {
+ first = false;
+ fprintf(args_out, "%s\033[00m\n", *(jump + j));
+ }
+ else
+ fprintf(args_out, "%s\033[%sm%s\033[00m\n", empty, colour, *(jump + j));
+ free(buf);
+ free(jumps);
+ }
+ }
+
+ free(empty);
+ free(lines);
+ free(lens);
+ fprintf(args_out, "\n");
+}
+
+
+/**
+ * Parse arguments
+ *
+ * @param argc The number of elements in `argv`
+ * @param argv The command line arguments, it should include the execute file at index 0
+ * @return Whether no unrecognised option is used
+ */
+extern long args_parse(int argc, char** argv)
+{
+ /* TODO */
+}
+
+
+/**
+ * Compare two strings
+ *
+ * @param a -1 if returned if this sting is the alphabetically lesser one
+ * @param b 1 if returned if this sting is the alphabetically lesser one
+ * @return 0 is returned if the two string are identical, other -1 or 1 is returned
+ */
+static long cmp(char* a, char* b)
+{
+ char c;
+ while (*a && *b)
+ {
+ if ((c = (*a < *b ? -1 : (*a > *b ? 1 : 0))))
+ return c;
+ a++;
+ b++;
+ }
+ return *a < *b ? -1 : (*a > *b ? 1 : 0);
+}
+
+
+/**
+ * Naïve merge sort is best merge sort in C
+ *
+ * @param list The list to sort from the point that needs sorting
+ * @param count The number of elements to sort
+ * @param temp Auxiliary memory
+ */
+static void _sort(char** list, long count, char** temp)
+{
+ if (count > 1)
+ {
+ long i = 0, a = count >> 1;
+ long j = a, b = count - a;
+ sort(list + 0, a, temp + 0);
+ sort(list + a, b, temp + a);
+ b += a;
+ while ((i < a) && (j < b))
+ {
+ char c = cmp(*(temp + i), *(temp + j));
+ if (c <= 0)
+ *list++ = *(temp + i++);
+ else
+ *list++ = *(temp + j++);
+ }
+ while (i < a)
+ *list++ = *(temp + i++);
+ while (j < b)
+ *list++ = *(temp + j++);
+ list -= count;
+ for (i = 0; i < count; i++)
+ *(temp + i) = *(list + i);
+ }
+ else if (count == 1)
+ *temp = *list;
+}
+
+
+/**
+ * Naïve merge sort is best merge sort in C
+ *
+ * @param list The list to sort
+ * @param count The number of elements to sort
+ */
+static void sort(char** list, long count)
+{
+ char** temp = (char**)malloc(count * sizeof(char*));
+ _sort(list, count, temp);
+ free(temp);
}