diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/argparser.c | 681 |
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); } |