namespace ParseArgs { import File; import String; public typedef union { &bool arg_flag; &string arg_string; &int arg_int; &real arg_real; (void(string)) arg_lambda; } arg_var; public typedef struct { arg_var var; int abbr; string name; string expr_name; string desc; } arg; public typedef struct { arg_var var; void optional; string name; } posn_arg; public typedef struct { string prog_name; arg[*] args; posn_arg[*] posn_args; &int unknown; } argdesc; string arg_string(arg a) /* * Given an arg desc, produce * a printable representation */ { string stdarg() { if (is_uninit(&a.name)) return a.expr_name; string s = ""; if (!is_uninit(&a.abbr)) s += sprintf("-%c,", a.abbr); s += sprintf("--%s", a.name); return s; } union switch (a.var) { case arg_flag: return stdarg(); default: return sprintf("%s <%s>", stdarg(), a.expr_name); } abort("internal error: unknown arg type"); } public string basename(string path) /* * Return the filename part of 'path' */ { int p = rindex(path, "/"); if (p == -1) return path; return substr(path, p + 1, length(path) - p - 1); } /* * The argument description was defective, with * a reason string. */ public exception bad_argd(string reason); /* * Print a usage message on the given file. * XXX Valid only after parseargs() has been called. */ public void(file f) usage; public void parseargs(&argdesc argd, &(string[*]) argv) /* * Parse arguments in 'argv' according to 'argd' */ { /* * validate that any argd.posn_args * consists of mandatory followed by optional args */ if (!is_uninit(&argd.posn_args)) for (int i = 1; i < dim(argd.posn_args); i++) if (!is_uninit(&argd.posn_args[i - 1].optional) && is_uninit(&argd.posn_args[i].optional)) raise bad_argd("fixed after optional posn arg"); /* argument that is current focus */ int argind = 0; /* save program basename as needed */ if (is_uninit(&argd.prog_name)) { argd.prog_name = basename(argv[0]); argind++; } /* build hashes for flags */ arg[int] arg_abbr_hash = {}; arg[string] arg_name_hash = {}; if (!is_uninit(&argd.args)) { for (int i = 0; i < dim(argd.args); i++) { &arg d = &argd.args[i]; if (!is_uninit(&d.abbr)) arg_abbr_hash[d.abbr] = d; if (!is_uninit(&d.name)) arg_name_hash[d.name] = d; } } void usagef(file f) /* * Print a description of program usage on f */ { fprintf(f, "%s: usage: %s", argd.prog_name, argd.prog_name); if (!is_uninit(&argd.args)) { fprintf(f, " "); for (int i = 0; i < dim(argd.args); i++) if (is_uninit(&argd.args[i].name)) fprintf(f, " %s", arg_string(argd.args[i])); } if (!is_uninit(&argd.posn_args)) { for (int i = 0; i < dim(argd.posn_args); i++) if (is_uninit(&argd.posn_args[i].optional)) fprintf(f, " %s", argd.posn_args[i].name); else fprintf(f, " [%s]", argd.posn_args[i].name); } if (!is_uninit(&&argd.unknown)) fprintf(f, " [--] ..."); fprintf(f, "\n"); if (!is_uninit(&argd.args)) { for (int i = 0; i < dim(argd.args); i++) if (is_uninit(&argd.args[i].name)) fprintf(f, " %s %s\n", argd.args[i].expr_name, argd.args[i].desc); for (int i = 0; i < dim(argd.args); i++) if (!is_uninit(&argd.args[i].name)) fprintf(f, " %s %s\n", arg_string(argd.args[i]), argd.args[i].desc); } exit(1); } usage = usagef; void save_value(&arg_var a, string value) /* * convert and store the value * associated with an argument. * return number of args consumed */ { union switch(a) { case arg_string var: var = value; break; case arg_int var: var = string_to_integer(value); break; case arg_real var: var = string_to_real(value); break; case arg_lambda var: var(value); break; default: abort("save_value called on wrong var type"); } } void process_flag(&arg a) /* * Process a flag */ { argind++; union switch(a.var) { case arg_flag var: var = true; return; default: if (argind >= dim(argv)) { fprintf(stderr, "%s: missing value for --%s\n", argd.prog_name, a.name); usagef(stderr); } save_value(&a.var, argv[argind]); argind++; } } /* handle the help case first */ if (argind < dim(argv) && (argv[argind] == "--help" || argv[argind] == "--usage")) usagef(stdout); /* now walk the arguments */ while(argind < dim(argv)) { string a = argv[argind]; /* we're done on a "user stop" */ if (a == "--") { if (is_uninit(&&argd.unknown)) { fprintf(stderr, "%s: don't know what to do with arg \"%s\"\n", argd.prog_name, a); usagef(stderr); } break; } /* process abbreviated (old-style) flags (maybe grouped) */ if (a[0] == '-' && a[1] != '-') { for (int i = 1; i < length(a); i++) { if (!hash_test(arg_abbr_hash, a[i])) { fprintf(stderr, "%s: unknown flag char '-%c'\n", argd.prog_name, a[i]); usagef(stderr); } process_flag(&arg_abbr_hash[a[i]]); } continue; } /* process a long flag */ if (a[0] == '-' && a[1] == '-') { string argname = substr(a, 2, length(a) - 2); if (!hash_test(arg_name_hash, argname)) { fprintf(stderr, "%s: unknown flag \"--%s\"\n", argd.prog_name, argname); usagef(stderr); } process_flag(&arg_name_hash[argname]); continue; } /* maybe something will suck up remaining args */ if (!is_uninit(&argd.posn_args) || !is_uninit(&&argd.unknown)) break; fprintf(stderr, "%s: extra argument \"%s\"\n", argd.prog_name, argv[argind]); usagef(stderr); } /* process any positional arguments */ if (!is_uninit(&argd.posn_args)) { for (int i = 0; i < dim(argd.posn_args); i++) { if (argind >= dim(argv) || argv[argind] == "--") { if (!is_uninit(&argd.posn_args[i].optional)) break; fprintf(stderr, "%s: missing required argument %s\n", argd.prog_name, argd.posn_args[i].name); usagef(stderr); } save_value(&argd.posn_args[i].var, argv[argind]); argind++; } } /* process any unknown arguments */ if (argind < dim(argv)) { if (argv[argind] == "--") argind++; if (!is_uninit(&&argd.unknown)) { argd.unknown = argind; return; } if (argind < dim(argv)) { fprintf(stderr, "%s: unknown or extra argument \"%s\"\n", argd.prog_name, argv[argind]); usagef(stderr); } } } }