/* * dnsutl - utilities to make DNS easier to configure * Copyright (C) 1991-1993, 1995, 1999, 2006, 2007 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 static arglex_table_ty table[] = { { "-", arglex_token_stdio }, { "-Help", arglex_token_help }, { "-VERSion", arglex_token_version }, { 0, 0 } /* end marker */ }; static int argc; static char **argv; arglex_value_ty arglex_value; arglex_token_ty arglex_token; char *progname; static arglex_table_ty *utable; static const char *partial; static char * base_name(char *s) { char *bp; char *ep; bp = s; ep = 0; while (*s) { if (s[0] == '/' && s[1] && s[1] != '/') bp = s + 1; if (s > bp && s[0] == '/' && s[-1] != '/') ep = s; ++s; } if (!ep) ep = s; *s = 0; return bp; } void arglex_init(int ac, char **av, arglex_table_ty * tp) { progname = base_name(av[0]); argc = ac - 1; argv = av + 1; utable = tp; } int argcmp(const char *formal, const char *actual) { char fc; char ac; int result; for (;;) { ac = *actual++; if (isupper(ac)) ac = tolower(ac); fc = *formal++; switch (fc) { case 0: result = !ac; goto ret; case '_': if (ac == '-') break; /* fall through... */ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': /* * optional characters */ if (ac == fc && argcmp(formal, actual)) { result = 1; goto ret; } /* * skip forward to next * mandatory character, or after '_' */ while (islower(*formal)) ++formal; if (*formal == '_') { ++formal; if (ac == '_' || ac == '-') ++actual; } --actual; break; case '*': /* * This is a hack, it should really * check for a match match the stuff after * the '*', too, a la glob. */ if (!ac) { result = 0; goto ret; } partial = actual - 1; result = 1; goto ret; case '\\': if (actual[-1] != *formal++) { result = 0; goto ret; } break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': fc = tolower(fc); /* fall through... */ default: /* * mandatory characters */ if (fc != ac) { result = 0; goto ret; } break; } } ret: return result; } static int is_a_number(const char *s) { long n; int sign; n = 0; switch (*s) { default: sign = 1; break; case '+': s++; sign = 1; break; case '-': s++; 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; } arglex_token_ty arglex(void) { arglex_table_ty *tp; int j; arglex_table_ty *hit[20]; int nhit; static const char *pushback[3]; static int pushback_depth; const char *arg; if (pushback_depth) { /* * the second half of a "-foo=bar" style argument, usually */ arg = pushback[--pushback_depth]; } else { if (argc <= 0) { arglex_token = arglex_token_eoln; arg = ""; goto ret; } arg = argv[0]; argc--; argv++; /* * 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] != '=') { char *eqp; eqp = strchr(arg, '='); if (eqp) { pushback[pushback_depth++] = eqp + 1; *eqp = 0; } } /* * Turn the GNU-style leading "--" * into "-" if necessary. */ if (arg[0] == '-' && arg[1] == '-' && arg[2] && !is_a_number(arg + 1)) ++arg; } if (is_a_number(arg)) { arglex_token = arglex_token_number; goto ret; } nhit = 0; partial = 0; for (tp = table; tp->name; tp++) { if (argcmp(tp->name, arg)) hit[nhit++] = tp; } if (utable) { for (tp = utable; tp->name; tp++) { if (argcmp(tp->name, arg)) hit[nhit++] = tp; } } switch (nhit) { case 0: break; case 1: if (partial) pushback[pushback_depth++] = partial; arg = hit[0]->name; arglex_token = hit[0]->token; goto ret; default: { size_t len; char *buf; len = strlen(hit[0]->name + 1); for (j = 1; j < nhit; ++j) len += strlen(hit[j]->name) + 2; buf = mem_alloc(len); strcpy(buf, hit[0]->name); for (j = 1; j < nhit; ++j) { strcat(buf, ", "); strcat(buf, hit[j]->name); } fatal("option \"%s\" ambiguous (%s)", arg, buf); } } /* not found in the table */ if (arg[0] == '-') arglex_token = arglex_token_option; else arglex_token = arglex_token_string; ret: #if 0 printf("arglex %d\n", arglex_token); #endif arglex_value.alv_string = arg; return arglex_token; }