Logo Search packages:      
Sourcecode: fhist version File versions

arglex.c

/*
 *    fhist - file history and comparison tools
 *    Copyright (C) 1991-1994, 1998, 2000-2002 Peter Miller;
 *    All rights reserved.
 *
 *    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 2 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, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 *
 * MANIFEST: functions to perform lexical analysis on command line arguments
 */

#include <ac/stddef.h>
#include <ac/stdlib.h>
#include <ac/string.h>
#include <ac/ctype.h>
#include <math.h>

#include <arglex.h>
#include <cmalloc.h>
#include <error_intl.h>
#include <language.h>
#include <progname.h>
#include <sub.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;
static arglex_table_ty *utable;
static const char *partial;


void
arglex_init(int ac, char **av, arglex_table_ty *tp)
{
      progname_set(av[0]);
      language_init();

      argc = ac - 1;
      argv = av + 1;
      utable = tp;
}


int
arglex_compare(const char *formal, const char *actual)
{
      char        fc;
      char        ac;
      int         result;

      for (;;)
      {
            ac = *actual++;
            if (isupper((unsigned char)ac))
                  ac = tolower((unsigned char)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 && arglex_compare(formal, actual))
                  {
                        result = 1;
                        goto ret;
                  }
                  /*
                   * skip forward to next
                   * mandatory character, or after '_'
                   */
                  while (islower((unsigned char)*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((unsigned char)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 char *pushback;
      char        *arg;

      if (pushback)
      {
            /*
             * the second half of a "-foo=bar" style argument.
             */
            arg = pushback;
            pushback = 0;
      }
      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 = 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 (arglex_compare(tp->name, arg))
                  hit[nhit++] = tp;
      }
      if (utable)
      {
            for (tp = utable; tp->name; tp++)
            {
                  if (arglex_compare(tp->name, arg))
                        hit[nhit++] = tp;
            }
      }
      switch (nhit)
      {
      case 0:
            /*
             * not found in the table
             */
            if (arg[0] == '-')
                  arglex_token = arglex_token_option;
            else
                  arglex_token = arglex_token_string;
            break;

      case 1:
            if (partial)
                  arg = (char *)partial;
            else
                  arg = hit[0]->name;
            arglex_token = hit[0]->token;
            break;

      default:
            {
                  size_t            len;
                  char        *buf;
                  sub_context_ty    *scp;

                  len = strlen(hit[0]->name + 1);
                  for (j = 1; j < nhit; ++j)
                        len += strlen(hit[j]->name) + 2;
                  buf = r_alloc_and_check(len);
                  strcpy(buf, hit[0]->name);
                  for (j = 1; j < nhit; ++j)
                  {
                        strcat(buf, ", ");
                        strcat(buf, hit[j]->name);
                  }

                  scp = sub_context_new();
                  sub_var_set_charstar(scp, "Name", arg);
                  sub_var_set_charstar(scp, "Guess", buf);
                  fatal_intl(scp, i18n("option \"$name\" ambiguous ($guess)"));
                  /* NOTREACHED */
            }
      }

ret:
      arglex_value.alv_string = arg;
      return arglex_token;
}


char *
arglex_token_name(arglex_token_ty 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->token == n)
                  return tp->name;
      }
      if (utable)
      {
            for (tp = utable; tp->name; tp++)
            {
                  if (tp->token == n)
                        return tp->name;
            }
      }

      return "unknown command line token";
}

Generated by  Doxygen 1.6.0   Back to index