/*
* 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
* <http://www.gnu.org/licenses/>.
*/
#include <ac/ctype.h>
#include <ac/stddef.h>
#include <ac/stdlib.h>
#include <ac/string.h>
#include <math.h>
#include <arglex.h>
#include <error.h>
#include <mem.h>
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;
}
syntax highlighted by Code2HTML, v. 0.9.1