// // aegis - project change supervisor // Copyright (C) 1991-1995, 1997-1999, 2001-2006, 2008 Peter Miller // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see // . // #include #include #include #include #include #include #include #include #include static arglex_table_ty table[] = { { "-", arglex_token_stdio, }, { "-Help", arglex_token_help, }, { "-VERSion", arglex_token_version, }, { "-TRAce", arglex_token_trace, }, { "-Page_Length", arglex_token_page_length, }, { "-Page_Width", arglex_token_page_width, }, }; typedef const char **argv_t; static int argc; static argv_t argv; arglex_value_ty arglex_value; int arglex_token; static arglex_table_ty *utable; static const char *partial; static int strings_only_mode; static int incomplete = -1; static int is_synthetic; // // NAME // arglex_init // // SYNOPSIS // void arglex_init(int ac, char **av, arglex_table-t *tp); // // DESCRIPTION // The arglex_init function is used to initialize the // command line processing. // // ARGUMENTS // ac - aergument count, from main // av - argument values, from main // tp - pointer to table of options // // CAVEAT // Must be called before the arglex() function. // void arglex_init(int ac, char **av, arglex_table_ty *tp) { progname_set(av[0]); argc = ac - 1; argv = (argv_t)(av + 1); // gcc incorrectly whines about incompatible // pointer types in assignment utable = tp; } // // NAME // is_a_number // // SYNOPSIS // int is_a_number(char *s); // // DESCRIPTION // The is_a_number function is used to determine if the // argument is a number. // // The value is placed in arglex_value.alv_number as // a side effect. // // Negative and positive signs are accepted. // The C conventions for decimal, octal and hexadecimal are understood. // // There may be no white space anywhere in the string, // and the string must end after the last digit. // Trailing garbage will be interpreted to mean it is not a string. // // ARGUMENTS // s - string to be tested and evaluated // // RETURNS // int; zero if not a number, // non-zero if is a number. // static int is_a_number(const char *s) { long n; int sign; n = 0; switch (*s) { case '-': ++s; sign = -1; break; case '+': ++s; sign = 1; break; default: sign = 1; break; } switch (*s) { case '0': if ((s[1] == 'x' || s[1] == 'X') && s[2]) { s += 2; for (;;) { switch (*s) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = n * 16 + *s++ - '0'; continue; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': n = n * 16 + *s++ - 'A' + 10; continue; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': n = n * 16 + *s++ - 'a' + 10; continue; } break; } } else { for (;;) { switch (*s) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': n = n * 8 + *s++ - '0'; continue; } break; } } break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': for (;;) { switch (*s) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = n * 10 + *s++ - '0'; continue; } break; } break; default: return 0; } if (*s) return 0; arglex_value.alv_number = n * sign; return 1; } // // NAME // arglex // // SYNOPSIS // int arglex(void); // // DESCRIPTION // The arglex function is used to perfom lexical analysis // on the command line arguments. // // Unrecognised options are returned as arglex_token_option // for anything starting with a '-', or // arglex_token_string otherwise. // // RETURNS // The next token in the token stream. // When the end is reached, arglex_token_eoln is returned forever. // // CAVEAT // Must call arglex_init befor this function is called. // int arglex(void) { arglex_table_ty *tp; int j; arglex_table_ty *hit[20]; int nhit; static const char *pushback; const char *arg; string_ty *s1; string_ty *s2; int is_inco; trace(("arglex()\n{\n")); if (pushback) { // // the second half of a "-foo=bar" style argument. // arg = pushback; is_inco = (incomplete == 0); pushback = 0; } else { get_another: if (argc <= 0) { arglex_token = arglex_token_eoln; arg = ""; goto done; } arg = argv[0]; is_inco = (incomplete == 0); argc--; argv++; incomplete--; // // The "--" option means the rest of the arguments on // the command line are only strings. // if (strings_only_mode) { arglex_token = ( is_inco ? arglex_token_string_incomplete : arglex_token_string ); goto done; } if (arg[0] == '-' && arg[1] == '-' && !arg[2]) { strings_only_mode = 1; goto get_another; } // // See if it looks like a GNU "-foo=bar" option. // Split it at the '=' to make it something the // rest of the code understands. // if (arg[0] == '-' && arg[1] != '=') { const char *eqp = strchr(arg, '='); if (eqp) { pushback = eqp + 1; if (is_inco) { incomplete = 0; is_inco = 0; } // // Crop the argument, so that we can figure out that the // left hand side means. It is often a memory leak. // arg = mem_copy_string(arg, eqp - arg); } } // // Turn the GNU-style leading "--" // into "-" if necessary. // if (arg[0] == '-' && arg[1] == '-' && arg[2] && !is_a_number(arg + 1)) ++arg; } // // see if it is a number // if (is_a_number(arg)) { arglex_token = (is_inco ? arglex_token_number_incomplete : arglex_token_number); goto done; } // // scan the tables to see what it matches // nhit = 0; partial = 0; if (!is_inco) { for (tp = table; tp < ENDOF(table); tp++) { if (arglex_compare(tp->t_name, arg, &partial)) hit[nhit++] = tp; } if (utable) { for (tp = utable; tp->t_name; tp++) { if (arglex_compare(tp->t_name, arg, &partial)) hit[nhit++] = tp; } } } // // deal with unknown or ambiguous options // switch (nhit) { case 0: // // not found in the tables // if (*arg == '-') { arglex_token = ( is_inco ? arglex_token_option_incomplete : arglex_token_option ); } else { arglex_token = ( is_inco ? arglex_token_string_incomplete : arglex_token_string ); } break; case 1: one: arglex_token = hit[0]->t_token; if (partial) arg = partial; // const-ness hack else arg = hit[0]->t_name; break; default: // // not an error if they are all the same // e.g. due to cultural spelling differences // with the same abbreviation. // for (j = 1; j < nhit; ++j) if (hit[0]->t_token != hit[j]->t_token) break; if (j >= nhit) goto one; if (is_synthetic) { arglex_token = arglex_token_option; goto done; } // // build a list of the names // and complain that it is ambiguous // s1 = str_from_c(hit[0]->t_name); for (j = 1; j < nhit; ++j) { s2 = str_format("%s, %s", s1->str_text, hit[j]->t_name); str_free(s1); s1 = s2; } fatal_raw("option \"%s\" ambiguous (%s)", arg, s1->str_text); } // // here for all exits // done: arglex_value.alv_string = arg; trace(("return %d; /* %s */\n", arglex_token, arglex_value.alv_string)); trace(("}\n")); return arglex_token; } // // NAME // arglex_prefetch // // SYNOPSIS // int arglex_prefetch(int *list, int list_len); // // DESCRIPTION // The arglex_prefetch function is used to perfom lexical analysis // on the command line arguments, much like the arglex function. // However, it is given a list of tokens to look for on the command // line, and such arguments are matched and extracted, which may // be used to relax command line argument ordering restrictions. // // RETURNS // One of the tokens in the list, or // ARGLEX_PREFETCH_FAIL if none of the are available. // // CAVEAT // Must call arglex_init before this function is called. // int arglex_prefetch(int *list, int list_len) { int j; trace(("arglex_prefetch()\n{\n")); if (strings_only_mode) goto fail; for (j = 0; j < argc; ++j) { const char *actual; int k; if (j == incomplete) continue; // // The "--" option means the rest of the arguments on // the command line are only strings. // actual = argv[j]; if (actual[0] == '-' && actual[1] == '-' && !actual[2]) goto fail; // // see if it is a number // if (is_a_number(actual)) continue; // // Turn the GNU-style leading "--" // into "-" if necessary. // if ( actual[0] == '-' && actual[1] == '-' && actual[2] && !is_a_number(actual + 1) ) ++actual; for (k = 0; k < list_len; ++k) { int token; const char *formal; token = list[k]; formal = arglex_token_name(token); if (arglex_compare(formal, actual, &partial)) { int m; // // Shuffle everything down to fill in // the hole. // for (m = j + 1; m < argc; ++m) argv[m - 1] = argv[m]; --argc; if (j < incomplete) --incomplete; // // Fill in the answer as it would be // returned form arglex() // arglex_value.alv_string = actual; arglex_token = token; trace(("return %d; /* %s */\n", arglex_token, arglex_value.alv_string)); trace(("}\n")); return arglex_token; } } } fail: trace(("return FAIL;\n")); trace(("}\n")); return ARGLEX_PREFETCH_FAIL; } const char * arglex_token_name(int n) { arglex_table_ty *tp; switch (n) { case arglex_token_eoln: return "end of command line"; case arglex_token_number: return "number"; case arglex_token_option: return "option"; case arglex_token_stdio: return "standard input or output"; case arglex_token_string: return "string"; default: break; } for (tp = table; tp < ENDOF(table); tp++) { if (tp->t_token == n) return tp->t_name; } if (utable) { for (tp = utable; tp->t_name; tp++) { if (tp->t_token == n) return tp->t_name; } } assert(0); return "unknown command line token"; } arglex_table_ty * arglex_table_catenate(arglex_table_ty *tp1, arglex_table_ty *tp2) { size_t len1; size_t len2; size_t len; arglex_table_ty *tp; static arglex_table_ty zero = ARGLEX_END_MARKER; for (len1 = 0; tp1[len1].t_name; ++len1) ; for (len2 = 0; tp2[len2].t_name; ++len2) ; len = len1 + len2; tp = (arglex_table_ty *)mem_alloc((len + 1) * sizeof(arglex_table_ty)); memcpy(tp, tp1, len1 * sizeof(arglex_table_ty)); memcpy(tp + len1, tp2, len2 * sizeof(arglex_table_ty)); tp[len] = zero; return tp; } // // NAME // arglex_dispatch // // DESCRIPTION // The arglex_dispatch function is used to dispatch into a function // table, based on the presence of one of a given list of command // line arguments being poresent on the command line. // // If none of them are present, the `the_default' argument (if non-zero) // is a default function to be called. // void arglex_dispatch(arglex_dispatch_ty *choices, int choices_len, void (*the_default)(void)) { int j; int *tmp; int tmp_len; int tok; int priority; trace(("arglex_dispatch()\n{\n")); tmp = (int *)mem_alloc(choices_len * sizeof(tmp[0])); for (priority = 0;; ++priority) { tmp_len = 0; for (j = 0; j < choices_len; ++j) { arglex_dispatch_ty *cp = choices + j; if (cp->priority == priority) tmp[tmp_len++] = cp->token; } if (tmp_len == 0) break; tok = arglex_prefetch(tmp, tmp_len); for (j = 0; j < choices_len; ++j) { arglex_dispatch_ty *cp = choices + j; if (tok == cp->token) { mem_free(tmp); if (cp->func) cp->func(); trace(("}\n")); return; } } } mem_free(tmp); if (the_default) the_default(); trace(("}\n")); } void arglex_synthetic(int ac, char **av, int inco) { argc = ac - 1; argv = (argv_t)(av + 1); // gcc incorrectly whines about incompatible // pointer types in assignment is_synthetic = 1; incomplete = inco - 1; } void arglex_retable(arglex_table_ty *tp) { utable = tp; } int arglex_get_string(void) { int result; int hold; hold = strings_only_mode; strings_only_mode = 1; result = arglex(); strings_only_mode = hold; return result; }