// $Id: parser.cpp,v 1.12 2014/05/21 20:05:57 lpoulsen Exp $
//
// Copyright (c) 2008 by AFAR Communications Inc, Santa Barbara, CA 93117, USA
//
// Command parser - version for Windows PC programs
// Ported from Afar's embedded code tree May 2008
//
// $Log: parser.cpp,v $
// Revision 1.12  2014/05/21 20:05:57  lpoulsen
// uint32 is not long anymore, so remove long qualifiers in printf formats
//
// Revision 1.11  2011/10/07 18:07:02  lpoulsen
// On system with just one ethernet port, don't require user to enter "1"
//
// Revision 1.10  2011/06/30 22:21:54  lpoulsen
// Add parser_empty() call for use in "set" command.
//
// Revision 1.9  2011/03/22 00:26:10  lpoulsen
// Resolve sequence errors on very weak links
// Resolve hangs when Ctrl-C between "dir" and "download"
// Resolve conflicting build date information
//
// Revision 1.8  2011/02/08 22:03:48  lpoulsen
// Clean up "ambiguous ELSE" warning
//
// Revision 1.7  2011/02/04 22:29:07  lpoulsen
// Many little fixes.
//
// Revision 1.6  2010/12/22 17:36:34  lpoulsen
// Select/Deselect list of ranges
// Expand to up to 511 radios
// Protocol fixes
//
// Revision 1.5  2008/07/30 07:11:42  lpoulsen
// New option "Connect keyboard=yes"
// Fixes for timeout probelm at end of file.
//
// Revision 1.4  2008/07/16 23:18:15  lpoulsen
// Correct error introduced in latest Linux mods with long string args
//
// Revision 1.3  2008/07/11 20:14:10  lpoulsen
// Resolve many compiler warnings on Linux
//
// Revision 1.2  2008/06/10 00:06:10  lpoulsen
// New -X option is now working
// Updated -I and -O implementation
//
// Revision 1.1  2008/05/31 00:35:09  lpoulsen
// Econ 4.x preview - now with command shell
//
//

#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
// #include <iostream>
// #include <ostream>
// #include <fstream>
// #include <iomanip>

#include "afartype.h"
#include "uciosess.h"
#include "parser.h"

#define      AOK 0

extern void *malloc(size_t s);
extern void  free(void *);

extern "C" int debug_level;

// using namespace std;

#undef ALLOW_OCTAL

cmd_ret_t
process_bitflags(const char *parsed_value, uint32 *result_mask,
                 const char *arg_name,
                 const Select *sel_list, ucio_session *session);

extern uint16 my_config_pwd_hash;  // in coredata.c

// Data for the values, arguments, and commands

const Arg CMD_ALIAS[] = {{ 0, {UNDEF_ARG} }};

const Arg no_args[]   = {{ 0, {UNDEF_ARG} }};

static const char * const bool_choices[] =
{
   // listed here in matching pairs.
   // even (left column) become 0
   // odd (right column) become 1
   "0",     "1",
   "off",   "on",
   "false", "true",
   "no",    "yes",
   0
};

int parser_key = 0xC8F5; // keyhash("wr2411")

#if 0
Argument_List::Argument_List() :
   param(0),
   value(0),
   next(0)
   {
   }

Argument_List::~Argument_List()
   {
   // assert((next == NULL) || uchw_is_ram(next, "Argument_List:next"));
   // assert((param == NULL) || uchw_is_ram(param, "Argument_List:param"));
   // assert((value == NULL) || uchw_is_ram(value, "Argument_List:value"));
   delete [] param;
   delete [] value;
   delete next;
   }
#endif

void *Argument_List::operator new(size_t s)
{
    void *result = malloc(s);
	if (result == NULL) {
		printf("malloc failed!!\n");
	}
    memset(result, 0, s);
    return result;
}

void Argument_List::operator delete(void *al, size_t s)
{
// For now, this is a no-op. A small memory leak, but so what
//    if (param) free(param);
//    if (value) free(value);
//    delete    next;

//    free(al);
}

void Argument_List::add_value(
   const char p[],
   const char v[])
   {
   Argument_List *n = this;
   while (n->next != 0)
      n = n->next;
   if ( (n->next = new Argument_List() ) == 0)
      {
      fprintf(stderr, "Out of memory\n");
      n->param = 0;
      n->value = 0;
      return;
      }
   n = n->next;

   // if ( (n->param = new char[(size_t)strlen(p) + 1]) == 0)
   if ((n->param = (char *)malloc((size_t)strlen(p) + 1)) == 0)
      {
      fprintf(stderr, "Out of memory\n");
      n->value = 0;
      return;
      }
   strcpy(n->param, p);

   // if ( (n->value = new char[(size_t)strlen(v) + 1]) == 0)
   if ((n->value = (char *)malloc((size_t)strlen(v) + 1)) == 0)
      {
      fprintf(stderr, "Out of memory\n");
      return;
      }
   strcpy(n->value, v);
   }

boolean Argument_List::empty() const
{
   return (next == NULL ? true : false);
}

const char *Argument_List::get_value(
   const char p[],
   const char d[]) const
   {
   if (param != 0 && strcmp(param, p) == 0)
      return value;
   else if (next != 0)
      return next->get_value(p, d);
   else
      return d;
   }


/* Convert the character its value as a digit, e.g. '0' => 0, 'A' => 10
Return -1 if the character is not a digit in any base */
static int char_to_digit(
   char c)
{
   static char digit[] = "0123456789ABCDEF";
   char *p = strchr(digit, (char)toupper(c) );
   return p == 0 ? -1 : p - digit;
}

// Is the character a valid digit in the given base?
static boolean is_digit(
   char c,
   int32 base)
   {
   int value = char_to_digit(c);
   return ((0 <= value) && (value < base) ? true : false);
   }

enum NUMBER_STATE
   {
   START,
   NON_DECIMAL,
   NUMBER,
   DONE
   };


/* RICKY */
#define IsDIGIT(chr)  (0x30<=(chr) && (chr)<=0x39 ? 1 : 0)
int32
parse_ipaddr( const char number[], const int32 def, boolean *errorp)
{
   if (number == 0)
      return def;

   unsigned char  *p;
   uint16         num = 0;
   uint32         u = 0;
   int         nDots = 0;
   int         nDigitsInGroup = 0;

    for (p = (unsigned char *)number; *p; ++p)
    {
        if (IsDIGIT(*p))
        {
            num = num * 10 + *p - '0';
            ++nDigitsInGroup;
        }
        else if (*p == '.')
        {
            if (++nDots > 3             ||
                	nDigitsInGroup == 0 ||
                	nDigitsInGroup > 3  ||
                	num > 255)
            	return 0;
            u = (u << 8) + num;
            num = 0;
            nDigitsInGroup = 0;
        }
        else
            return 0;
    }

    /* last group */
    if (nDots != 3              ||
        	nDigitsInGroup == 0 ||
        	nDigitsInGroup > 3  ||
        	num > 255)          
        return 0;
    u = (u << 8) + num;
    return u;

}



int32
parse_number( const char number[], const int32 def, boolean *errorp)
{
   if (number == 0)
      return def;

   int pos = 0;

   // Skip leading whitespace
   while (isspace(number[pos]) )
      pos++;

   // Grab leading - or +
   boolean negative = false;
   if (number[pos] == '-')
      {
      negative = true;
      pos++;
      }
   else if (number[pos] == '+')
      pos++;

   NUMBER_STATE state = START;
   uint32 base = 0;
   uint32 value = 0;
   boolean error = false;
   while (!error && state != DONE)
      {
      switch (state)
         {
      case START:
         if (number[pos] == '0')
            state = NON_DECIMAL;
         else
         if (is_digit(number[pos], 10) )
            {
            value = char_to_digit(number[pos]);
            base = 10;
            state = NUMBER;
            }
         else
            error = true;
         break;

      case NON_DECIMAL:
         if (toupper(number[pos]) == 'X')
            {
            base = 16;
            state = NUMBER;
            }
         else if (toupper(number[pos]) == 'B')
            {
            base = 2;
            state = NUMBER;
            }
#if ALLOW_OCTAL
         else if (is_digit(number[pos], 8) )
            {
            base = 8;
            value = char_to_digit(number[pos]);
            state = NUMBER;
            }
#else
         else if (is_digit(number[pos], 10) )
            {
            base = 10;
            value = char_to_digit(number[pos]);
            state = NUMBER;
            }
#endif
         else if (number[pos] == '\0')
            state = DONE;
         else
            error = true;
         break;

      case NUMBER:
         // Update the value of the number from the next digit
         if (is_digit(number[pos], base) )
            {
            uint32 newvalue = base * value + char_to_digit(number[pos]);
            // Check for overflow
            if (newvalue / base != value)
               error = true;
            else
               value = newvalue;
            }
         else
            state = DONE;
         break;

      default:
         break;
         }
      pos++;
      }

   // Junk at the end is an error
   if (number[pos - 1] != '\0')
      error = true;

   // Apply negative sign, if any
   if (negative)
   {
      // Check for overflow
      if (value > 0x80000000)
         error = true;
      else
         value = -value;
   }

   // Report error
   if (errorp)
      *errorp = error;

   // Return default value if error, actual value if not
   if (error)
      return def;
   else
      return value;
}

boolean
parse_boolean(const char *string)
{
    int j;

    j = parse_string_match(string, bool_choices);
    return (j & 1 ? true : false);
}

boolean
parse_bool(const char *number, const boolean def, boolean *errorp)
{
    int j;

    if ((number == NULL) || strlen(number) == 0)
        return def;

    j = parse_string_match(number, bool_choices);
    return (j & 1 ? true : false);
}

int
parse_string_match(const char *string, const char * const strings[])
{
   for (int i = 0; strings[i] != 0; i++)
      if (strcmp(string, strings[i]) == 0)
         return i;
   return -1;
}

int
parse_select_match(const char *string, const Select *sel_tab)
{
   for (int k = 0; sel_tab[k].string != NULL; k++)
      if (strcmp(string, sel_tab[k].string) == 0)
         return k;
   return -1;
}

uint32
parse_bitmask(const char *string, const Select *sel_tab)
{
    uint32 result;

    cmd_ret_t idsw = process_bitflags(string, &result, NULL,
                                    sel_tab, NULL);
    if (idsw == CMD_BAD_ARG_VALUE) {
        fprintf(stderr,
                "Bad value \"%s\" for bitflag - this bitmask value ignored\n",
                string);
        return 0;
    }
    assert(idsw == CMD_SUCCESS);
    return result;
}

enum COMPARISON { EXACT, ABBR, NO_MATCH = ~AOK };

/* Compare two words, and return EXACT if all characters in both words are
the same, ABBR if the abbreviated word is a legal abbreviation of the
full word, or NO_MATCH if the words do not match.
The comparison is case sensitive. */

static COMPARISON
abbr_compare(
   const char *abbr,    // The abbreviated word
   const char *comp)    // The full word we are comparing the abbreviate word to
{
   int abbr_pos = 0;       // The current scan position of the abbreviated word
   int comp_pos = 0;       // The current scan position of the full word
   boolean abbr_scan = false; // Have we scanned the first letter in the current word?
   COMPARISON retval = EXACT; // The best possible return value so far
   int first_let = 0;      // The position of first letter of the last word we have scanned in the abbr

   while (true)
   {
      if (abbr[abbr_pos] == comp[comp_pos])
         if (comp[comp_pos] == '\0')   // The end
            return retval;
         else                          // Not the end
            {
            boolean old_abbr_scan = abbr_scan;
            abbr_scan = (abbr[abbr_pos] == '-' ? false : true);
            if ((old_abbr_scan == false) && abbr_scan)
               first_let = abbr_pos;
            abbr_pos++;
            comp_pos++;
            }
      else if (abbr[abbr_pos] == '\0') // Abbr has ended
         return ABBR;
      else if (comp[comp_pos] == '\0') // Abbr is too long
         return NO_MATCH;
      else if (abbr[abbr_pos] == '-')  // Skip to next word
         {
         retval = ABBR;
         abbr_scan = false;
         while (comp[comp_pos] != '-' && comp[comp_pos] != '\0')
            comp_pos++;
         }
      else if (comp[comp_pos] == '-')  // Skip the -
         {
         retval = ABBR;
         abbr_scan = false;
         comp_pos++;
         }
      else if (abbr_scan)              // Skip to next word
         {
         retval = ABBR;
         abbr_scan = false;
         while (comp[comp_pos] != '-' && comp[comp_pos] != '\0')
            comp_pos++;
         }
      else if (abbr_pos - 1 > first_let && abbr[abbr_pos - 1] != '-') // Backtrack
         abbr_pos--;
      else
         return NO_MATCH;
#ifdef WATCH_THE_COMPARISON_PROCESS
      int i;
      for (i = 0; i < comp_pos; i++)
         cout << comp[i];
      cout << endl;
      for (i = 0; i < abbr_pos; i++)
         cout << abbr[i];
      cout << endl;
      cout << endl;
#endif
   }
}


int
parse_command(
    const char cmd[], int &pos, const Command cmd_list[],
    ucio_session *session)
{
   // Skip over whitespace
   while (isspace(cmd[pos]) )
      pos++;

   // Get lowercase of command
   int len;
   char parsed_command[COMMAND_LEN];
   for (len = 0; (!isspace(cmd[pos])) && (cmd[pos] != '\0'); len++, pos++)
      parsed_command[len] = tolower(cmd[pos]);
   parsed_command[len] = '\0';

   // Find proper command using abbreviations
   int matches = 0;
   int cmd_index = -1;
   COMPARISON c;
   for (int i = 0; cmd_list[i].name; i++)
   {
      if (cmd_list[i].flags & PARSER_COMMENT)
         continue;      
      if ((cmd_list[i].flags & PARSER_HIDDEN)
       && (session->session_unlocked == 0) && (debug_level == 0))
         continue;
      if (cmd_list[i].flags & PARSER_DISABLED)
        continue;
      if ( (c = abbr_compare(parsed_command, cmd_list[i].name) ) != NO_MATCH)
         {
         int old_cmd_index = cmd_index;
         cmd_index = i;
         while (cmd_list[cmd_index].arg == CMD_ALIAS)
            cmd_index--;
         if (c == EXACT)
            {
            matches = 1;
            break;
            }
         else if (cmd_index != old_cmd_index)
            matches++;
         }
   }

   if (matches == 0)
      // No matches were found
      {
      fprintf(stderr, "Unknown command %s\n", parsed_command);
      fprintf(stderr, "Type help to list all commands\n");
      return ~AOK;
      }
   else if (matches > 1)
      // Abbreviation matches more than one command -- ambiguity
      {
      if (parsed_command[0] != '\0')
         {
         fprintf(stderr, "Ambiguous command %s matches\n", parsed_command);
         parse_list_matching_commands(parsed_command, cmd_list, session, false);
         }
      return ~AOK;
      }
   else
      // Exactly one command matches
      {
         if ((cmd_list[cmd_index].flags & PARSER_HIDDEN_CCFG)
          && (session->session_config == 0))
         {
            fprintf(stderr, "Configuration commands are LOCKED\n");
            return ~AOK;
         }
#if 0
         else if ((cmd_list[cmd_index].flags & PARSER_LOCAL_ONLY)
            && (!stream.islocal()))
         {
            fprintf(stderr, "This command is only valid on a local terminal\n");
            return ~AOK;
         }
#endif
         else {
            return cmd_index;
         }
      }
}

static void
parser_help_one_command(
   const Command cmd_list[],
   const int group,
   const int i,
   ucio_session *session, boolean fullmode)
{
   int j;
   int k;

         if (group >= 0)
            fprintf(stderr, " [%s]\n", cmd_list[group].name);

         fprintf(stderr, "  %s", cmd_list[i].name);
         if (fullmode && session->session_unlocked && cmd_list[i].flags) 
         {
            fprintf(stderr, "        ( ");
            if (cmd_list[i].flags & PARSER_NOHELP)   fprintf(stderr, "nohelp ");
            if (cmd_list[i].flags & PARSER_HIDDEN)   fprintf(stderr, "hidden ");
            if (cmd_list[i].flags & PARSER_HIDDEN_CCFG) fprintf(stderr, "config ");
            if (cmd_list[i].flags & PARSER_LOCAL_ONLY) fprintf(stderr, "local-only ");
            if (cmd_list[i].flags & PARSER_DISABLED)   fprintf(stderr, "disabled ");
            fprintf(stderr, ")");
         }
         fprintf(stderr, "\n");

         // NULL for argument list works like no_args[]
         if (cmd_list[i].arg == NULL)
         {
            fprintf(stderr, "\n");
            return;
         }

         int actual = i;
         while (cmd_list[actual].arg == CMD_ALIAS)
            actual--;

         for (j = 0; cmd_list[actual].arg[j].param != 0; j++)
         {
            int actual_param = j;

            if ((fullmode == false) || (session->session_unlocked == 0))
            {
               if (cmd_list[actual].arg[j].flags & PARSER_NOHELP)
                  continue;
               if ((cmd_list[actual].arg[j].flags & PARSER_HIDDEN)
                && (session->session_unlocked == 0) && (debug_level == 0))
                  continue;
               if ((cmd_list[actual].arg[j].flags & PARSER_HIDDEN_CCFG)
                && (session->session_config == 0))
                  continue;
               if (cmd_list[actual].arg[j].flags & PARSER_DISABLED)
                  continue;
            }
            fprintf(stderr, "    ");

            if ((cmd_list[actual].arg[j + 1].param != 0)
             && (cmd_list[actual].arg[j + 1].value.kind == ALIAS_ARG))
            {
               if ((cmd_list[actual].arg[j+1].flags & PARSER_NOHELP)
                && ((fullmode == false) || session->session_unlocked == 0))
               {
                   while (cmd_list[actual].arg[j+1].value.kind == ALIAS_ARG)
                        j++;
                   goto normal_param;
               }
               fprintf(stderr, " ");
               char  c = '{';
               for (k = j; k == j || cmd_list[actual].arg[k].value.kind == ALIAS_ARG; k++)
               {
                  fprintf(stderr, "%c%s", c, cmd_list[actual].arg[k].param);
                  if ((cmd_list[actual].arg[k].flags & PARSER_NOHELP)
                   && fullmode && session->session_unlocked)
                     fprintf(stderr, "[nohelp]");
                  c = '|';
               }
               fprintf(stderr, "}");
               j = k - 1;
            }
            else
            {
            normal_param:
               fprintf(stderr, " %s", cmd_list[actual].arg[actual_param].param);
            }
            uint32 lo = cmd_list[actual].arg[actual_param].value.lo;
            uint32 hi = cmd_list[actual].arg[actual_param].value.hi;
            Parser_Value_Kind kind = cmd_list[actual].arg[actual_param].value.kind;
            switch(kind)
            {
            case INT_ARG:
               if ((lo < hi) && ((int32)lo > (int32) hi))
                   fprintf(stderr, "= %u..%u", lo, hi);
               else
                   fprintf(stderr, "= %d..%d", (int32)lo, (int32)hi);
               break;
            case BOOL_ARG:
               fprintf(stderr, "={Boolean}");
               break;
            case INT2_ARG:
               if (lo < hi)
                   fprintf(stderr, "={%u..%u}", lo, hi);
               else
                   fprintf(stderr, "={%d..%d}", (int32)lo, (int32)hi);
               break;
            case STRING_ARG:
               fprintf(stderr, "=<string>");
               break;
            case CHOICE_ARG:
               {
                  char **ch_list = (char **)lo;
                  int k;

                  fprintf(stderr, "={");
                  for (k = 0; ch_list[k]; k++)
                  {
                     if (k) fprintf(stderr, "|");
                     fprintf(stderr, "%s", ch_list[k]);
                  }
                  fprintf(stderr, "}");
               }
               break;
            case SELECT_ARG:
               {
                  Select *sel_list = (Select *)lo;
                  int k, p = 0;

                  fprintf(stderr, "={");
                  for (k = 0; sel_list[k].string; k++)
                  {
                     if (sel_list[k].flags & PARSER_NOHELP)
                        continue;
                     if ((sel_list[k].flags & PARSER_HIDDEN)
                      && (session->session_unlocked == 0) && (debug_level == 0))
                        continue;
                     if ((sel_list[k].flags & PARSER_HIDDEN_CCFG)
                      && (session->session_config == 0))
                        continue;
                     if (sel_list[k].flags & PARSER_DISABLED)
                        continue;
                     if (p) fprintf(stderr, "|");
                     fprintf(stderr, "%s", sel_list[k].string);
                     p++;
                  }
                  fprintf(stderr, "}");
               }
               break;
            case BITS_ARG:
               {
                  Select *sel_list = (Select *)lo;
                  int k, p = 0;

                  fprintf(stderr, "=bitflags{");
                  for (k = 0; sel_list[k].string; k++)
                  {
                     if (sel_list[k].flags & PARSER_NOHELP)
                        continue;
                     if ((sel_list[k].flags & PARSER_HIDDEN)
                      && (session->session_unlocked == 0) && (debug_level == 0))
                        continue;
                     if ((sel_list[k].flags & PARSER_HIDDEN_CCFG)
                      && (session->session_config == 0))
                        continue;
                     if (sel_list[k].flags & PARSER_DISABLED)
                        continue;
                     if (p) fprintf(stderr, "|");
                     fprintf(stderr, "%s", sel_list[k].string);
                     p++;
                  }
                  fprintf(stderr, "}");
               }
               break;
            case IPADDR_ARG:
               fprintf(stderr, "=<ipaddr>");
               break;
            default:
               fprintf(stderr, "{unknown argument type}");
            } // end switch
            if (fullmode && session->session_unlocked
              && cmd_list[actual].arg[actual_param].flags)
            {
               fprintf(stderr, "        ( ");
               if (cmd_list[actual].arg[actual_param].flags & PARSER_NOHELP)
                  fprintf(stderr, "nohelp ");
               if (cmd_list[actual].arg[actual_param].flags & PARSER_HIDDEN)
                  fprintf(stderr, "hidden ");
               if (cmd_list[actual].arg[actual_param].flags
                                                       & PARSER_HIDDEN_CCFG)
                  fprintf(stderr, "config ");
               if (cmd_list[actual].arg[actual_param].flags & PARSER_DISABLED)
                  fprintf(stderr, "disabled ");
            fprintf(stderr, ")");
            }
            fprintf(stderr, "\n");
         }   // end for j=param
         fprintf(stderr, "\n");
}

cmd_ret_t
parse_list_matching_commands(
   const char command[],
   const Command cmd_list[],
   ucio_session *session, boolean fullmode)
{
   int matching = 0;
   int exact_match = -1;
   int first_match = -1;
   int exact_comment = -1;     
   int first_comment = -1;
   int latest_comment = -1;
   int onthisline = 0;
   COMPARISON c;
   int i;

   if (command == 0)
      command = "";

   for (i = 0; cmd_list[i].name; i++)
   {
      if (cmd_list[i].flags & PARSER_NOHELP)
         continue;
      if ((cmd_list[i].flags & PARSER_HIDDEN)
       && (session->session_unlocked == 0) && (debug_level == 0))
         continue;
      if (cmd_list[i].flags & PARSER_COMMENT)
      {
         latest_comment = i;
         continue;
      }
      if (cmd_list[i].flags & PARSER_DISABLED)
        continue;
      c = abbr_compare(command, cmd_list[i].name);
      if (c != NO_MATCH)
      {
         int actual = i;
         while (cmd_list[actual].arg == CMD_ALIAS)
            actual--;

         if (c == EXACT)
         {
            exact_match = actual;
            exact_comment = latest_comment;
         }
         if (matching == 0)
         {
            first_match = actual;
            first_comment = latest_comment;
         }
         matching++;
      } // end if compare matched
   }  // end for each command

   // matching is how many matches we have
   // if we have more than one, but one is an exact match,
   //    we need to give detailed help for that one AND list
   //    the other matches in brief form

   if (matching == 0)
   {
      fprintf(stderr, "No commands matching %s\n", command);
      return CMD_NOT_FOUND;
   }

   if (fullmode)
   {
       for (i = 0; cmd_list[i].name; i++)
       {
          if (cmd_list[i].flags & PARSER_COMMENT)
          {
             fprintf(stderr, "\n [%s]\n\n", cmd_list[i].name);
             continue;
          }
          c = abbr_compare(command, cmd_list[i].name);
          if (c != NO_MATCH)
          {
             parser_help_one_command(cmd_list, exact_comment,
                                     i, session, true);
          } // end if compare matched
       }  // end for each command 
       return CMD_SUCCESS;
   }

   if (exact_match >= 0)
   {
      parser_help_one_command(cmd_list, exact_comment,
                               exact_match, session, false);
      if (matching == 1)
         return CMD_SUCCESS;
      fprintf(stderr, "Other matches:\n");
   }
   else if (matching == 1)
   {
      for (i = 0; i <= first_match; i++)
         if (cmd_list[i].flags & PARSER_COMMENT)
            latest_comment = i;
      parser_help_one_command(cmd_list, latest_comment,
                               first_match, session, false);
      return CMD_SUCCESS;
   }

   // okay, ready for the second pass

   for (i = 0; cmd_list[i].name; i++)
   {
      if (cmd_list[i].flags & PARSER_NOHELP)
         continue;
      if ((cmd_list[i].flags & PARSER_HIDDEN)
       && (session->session_unlocked == 0) && (debug_level == 0))
         continue;
      if (cmd_list[i].flags & PARSER_COMMENT)
      {
         if (onthisline%3)
            fprintf(stderr, "\n");

         fprintf(stderr, " [%s]\n", cmd_list[i].name);
         onthisline = 0;
         continue;
      }
      if (cmd_list[i].flags & PARSER_DISABLED)
        continue;
      c = abbr_compare(command, cmd_list[i].name);
      if (c != NO_MATCH)
      {
         if (c == EXACT)
         {
            continue;
         }
         fprintf(stderr, "  %-24.24s", cmd_list[i].name);
         if ((++onthisline % 3) == 0) {
            fprintf(stderr, "\n");
         }
      } // end if compare matched
   }  // end for each command 
   if (onthisline%3)
      fprintf(stderr, "\n");

   return CMD_AMBIGUOUS;
}

// parse_arguments:
//     Pre-scan the argument list of the command and return a 
//       properly structured chain of normalized argument structures.
//     If no arguments were given, the chain has only one element:
//       The terminating entry.
//     If there is an error, this routine will print a message and
//       return NULL;

Argument_List *
parse_arguments(const int cmd_index, const char cmd[], int &pos,
    const Command cmd_list[], ucio_session *session)
{
    int status = AOK;
    int arg_num = 0;
    boolean error;
    Argument_List *p = new Argument_List();
    boolean named_parameter = false;
    uint32 n;

    if (p == 0)
    {
       fprintf(stderr, "Out of memory\n");
       return 0;
    }

    // For each argument, ...
    while (cmd[pos] != '\0')
    {
         int     len;
         char    parsed_arg[COMMAND_LEN];
         int     eq = -1;
         boolean quoted = false;

         arg_num++;
   
         // Skip over whitespace
         while (isspace(cmd[pos]) )
            pos++;
   
         // If there are no more arguments, leave
         if (cmd[pos] == '\0')
            break;
   
         // Get lowercase of argument

         if (cmd[pos] == '\"') {
            quoted = true;
            pos++;
            for (len = 0; (cmd[pos] != '\"') && cmd[pos] != '\0'; len++, pos++)
               parsed_arg[len] = cmd[pos];
            if (cmd[pos] == '\"') pos++;
         } else {
            for (len = 0; !isspace(cmd[pos]) && cmd[pos] != '\0'; len++, pos++)
            {
               if (cmd[pos] == '\"') {
                  quoted = true;
                  pos++;
                  for (; (cmd[pos] != '\"') && cmd[pos] != '\0'; len++, pos++)
                     parsed_arg[len] = cmd[pos];
                  if (cmd[pos] == '\"') pos++;
                  break;
               } else {
                  parsed_arg[len] = tolower(cmd[pos]);

                  if (cmd[pos] == '=')
                     eq = len;
               }
            }
         }
         parsed_arg[len] = '\0';
   
         // Find the parameter and value parts of the argument
         const char *parsed_param = parsed_arg;
         const char *parsed_value;
         boolean missing_value = false;

         if (eq < 0)
         {                        // Regex [^=]*       no equals sign at all
   #ifdef ONE_MISSING_PARAMETER
            /* If this is the first argument, and the command has only one
            argument, the value is the entire argument, and the parameter is
            the one parameter of the command. */
            if (arg_num == 1
             && cmd_list[cmd_index].arg[0].param != 0
             && cmd_list[cmd_index].arg[1].param == 0)
            {
               parsed_value = parsed_arg;
               parsed_param = cmd_list[cmd_index].arg[0].param;
            }
   #else // Many arguments can have missing parameters
            /* If we have not yet come across an argument with a named
               parameter, the value is the entire argument, and the
               parameter is the nth parameter in the command
               where n is the number of this argument. */

            if (!named_parameter)
            {
               parsed_value = parsed_arg;

               while ((cmd_list[cmd_index].arg[arg_num - 1].param)
                   && (cmd_list[cmd_index].arg[arg_num - 1].value.kind
                                                                 == ALIAS_ARG))
                   arg_num++;

               parsed_param = cmd_list[cmd_index].arg[arg_num - 1].param;
               if (parsed_param == 0)
               {
                  fprintf(stderr, "Too many arguments\n");
                  status = ~AOK;
                  break;   // Stop checking the arguments
               }
            }
   #endif
            else
            {
               status = ~AOK;
               fprintf(stderr, "Missing value in argument %s\n", parsed_arg);
               missing_value = true;
               // Keep parsing this argument to check the parameter and display
               // the valid values
            }
         }
         else if (eq == 0) 
         {            // Regex =.*         an equals sign is first character
            status = ~AOK;
            fprintf(stderr, "Missing parameter in argument %s\n", parsed_arg);
            fprintf(stderr, "Parameters are:\n");
            for (int i = 0; cmd_list[cmd_index].arg[i].param != 0; i++)
               fprintf(stderr, "  %s\n", cmd_list[cmd_index].arg[i].param);
            continue; // with the next argument
         }
         else if ((parsed_arg[eq + 1] == '\0') && (quoted == false))
            // Regex [^=].*=     the first equals sign is the last character
            //                       but not the first character
         {
            status = ~AOK;
            fprintf(stderr, "Missing value in argument %s\n", parsed_arg);
            missing_value = true;
            named_parameter = true;
            parsed_arg[eq] = '\0';
            parsed_value = &parsed_arg[eq + 1];
            // Keep parsing this argument to check the parameter and display
            // the valid values
         }
         else
         {  // Regex [^=]+=.+    stuff on each side of the first equals sign
            //                      (may be an empty quoted string on the right)
            named_parameter = true;
            parsed_arg[eq] = '\0';
            parsed_value = &parsed_arg[eq + 1];
         }
   
         // Find proper parameter using abbreviations
         int matches = 0;
         int arg_index = -1;
         COMPARISON c;
         for (int i = 0; cmd_list[cmd_index].arg[i].param != 0; i++)
            if ( (c = abbr_compare(parsed_param, cmd_list[cmd_index].arg[i].param) ) != NO_MATCH)
               {
               int old_arg_index = arg_index;
               arg_index = i;
               while (cmd_list[cmd_index].arg[arg_index].value.kind
                                                                  == ALIAS_ARG)
                  arg_index--;

               if (c == EXACT)
                  {
                  matches = 1;
                  break;
                  }
               else if (arg_index != old_arg_index)
                  matches++;
               }
   
         const char *param = 0;
         const char *value = 0;
         if (matches == 0)
            // No matches were found
            {
            fprintf(stderr, "Unknown parameter %s\n", parsed_param);
            fprintf(stderr, "Parameters are:\n");
            for (int i = 0; cmd_list[cmd_index].arg[i].param != 0; i++)
               fprintf(stderr, "  %s\n", cmd_list[cmd_index].arg[i].param);
            status = ~AOK;
            continue; // with the next argument
            }
         else if (matches > 1)
            // Abbreviation matches more than one parameter -- ambiguity
            {
            fprintf(stderr, "Ambiguous parameter %s matches\n", parsed_param);
            for (int i = 0; cmd_list[cmd_index].arg[i].param != 0; i++)
               if (abbr_compare(parsed_param, cmd_list[cmd_index].arg[i].param) != NO_MATCH)
                  fprintf(stderr, "  %s\n", cmd_list[cmd_index].arg[i].param);
            status = ~AOK;
            continue; // with the next argument
            }
   
         // Exactly one argument matches and the value is present
         // Make sure the parameter has been passed only once
         param = cmd_list[cmd_index].arg[arg_index].param;
         if (p->get_value(param) != 0)
            {
            fprintf(stderr, "Repeated parameter - %s\n", param);
            status = ~AOK;
            }
   
         uint32 lo = cmd_list[cmd_index].arg[arg_index].value.lo;
         uint32 hi = cmd_list[cmd_index].arg[arg_index].value.hi;
         Parser_Value_Kind kind = cmd_list[cmd_index].arg[arg_index].value.kind;

         uint32 flags = cmd_list[cmd_index].arg[arg_index].flags;
         if (((flags & PARSER_HIDDEN_CCFG) && (!session->session_config))
          || ((flags & PARSER_HIDDEN) && (!session->session_unlocked)
                                      && (debug_level == 0) )
          || (flags & PARSER_DISABLED) )
         {
            fprintf(stderr, "Illegal parameter - %s\n", parsed_arg);
            status = ~AOK;
            continue;
         }

         switch (kind)
         {
         case INT_ARG:
                  n = parse_number(parsed_value, 0, &error);
                  if (!missing_value && error)
                     {
                     fprintf(stderr, "Invalid number %s\n", parsed_value);
                     status = ~AOK;
                     }
                  else if ( (lo <  hi && !( lo <=  n &&  n <=  hi) )
                         || (lo >= hi && !( (int32)lo <= (int32)n
                                     && (int32)n <= (int32)hi) )
                        || missing_value)
                     {
                     if (!missing_value)
                        fprintf(stderr,
                                "Value %s out of range for parameter %s\n",
                               parsed_value,
                               cmd_list[cmd_index].arg[arg_index].param);
                     fprintf(stderr, "  Range is %u..%u\n", lo, hi);
                     status = ~AOK;
                     }
                  else
                     value = parsed_value;
               break;
   
         case INT2_ARG:
                  n = parse_number(parsed_value, 0, &error);
                  if (!missing_value && error)
                     {
                     fprintf(stderr, "Invalid number %s\n", parsed_value);
                     status = ~AOK;
                     }
                  else if (!(n == cmd_list[cmd_index].arg[arg_index].value.lo ||
                             n == cmd_list[cmd_index].arg[arg_index].value.hi) ||
                           missing_value)
                     {
                     if (!missing_value)
                        fprintf(stderr,
                                "Value %s out of range for parameter %s\n",
                                parsed_value,
                                cmd_list[cmd_index].arg[arg_index].param);
                     fprintf(stderr, "  Range is {%u|%u}\n",
                            cmd_list[cmd_index].arg[arg_index].value.lo,
                            cmd_list[cmd_index].arg[arg_index].value.hi);
                     status = ~AOK;
                     }
                  else
                     value = parsed_value;
             break;
   
         case BOOL_ARG:
            {
                int j;
                int match = -1;
   
                for (j = 0; bool_choices[j]; j++)
                {
                    switch (abbr_compare(parsed_value, bool_choices[j]))
                    {
                    case EXACT:
                        match = j;
                        break;
                    case ABBR:
                        if (match == -1) {
                            match = j;
                            value = bool_choices[j];
                        } else {
                            // already found a match; so it is ambiguous
                            value = NULL;
                            status = ~AOK;
                            continue;
                        }
                    case NO_MATCH:
                        continue;
                    }
                    // came out of the switch with a break - must be EXACT
                    value = bool_choices[j];
                    break;
                }
                if (match == -1) {
                    // Not a match
                    fprintf(stderr, "Invalid boolean value %s\n", parsed_value);
                    status = ~AOK;
                    break;
                }
                if (status == ~AOK) {
                    // Ambiguous value (i.e. "o" matches "on" and "off")
                    fprintf(stderr, "Ambiguous abbreviation %s\n", parsed_value);
                    break;
                }
                // That leaves exact match, fall through to break below
            }
            break;
   
         case STRING_ARG:
             if (missing_value)
                fprintf(stderr, "Value is a string\n");
             else
                value = parsed_value;
             break;
   
         case CHOICE_ARG:
            {
            char **ch_list = (char **)lo;
            COMPARISON c;
            int matches = 0;
            int j;
   
            for (j = 0; ch_list[j]; j++)
               {
               c = abbr_compare(parsed_value, ch_list[j]);
               if (c != NO_MATCH)
                  {
                  value = ch_list[j];
                  if (c == EXACT)
                     {
                     matches = 1;
                     break;
                     }
                  else
                     matches++;
                  }
               }
            if (matches == 0)
               {
               fprintf(stderr, "  Value %s not valid for %s\n",
                      parsed_value, cmd_list[cmd_index].arg[arg_index].param);
               fprintf(stderr, "  Valid values are {");
               for (j = 0; ch_list[j]; j++)
                  {
                  if (j) fprintf(stderr, "|");
                     fprintf(stderr, "%s", ch_list[j]);
                  }
               fprintf(stderr, "}\n");
               status = ~AOK;
               }
            else if (matches > 1)
               {
               fprintf(stderr, "  Value %s for %s is ambiguous, matches {",
                  parsed_value,  cmd_list[cmd_index].arg[arg_index].param);
               matches = 0;
               for (j = 0; ch_list[j]; j++)
                  {
                  c = abbr_compare(parsed_value, ch_list[j]);
                  if ( c != NO_MATCH )
                     {
                     if (matches)
                        fprintf(stderr, ", ");
                     fprintf(stderr, "%s", ch_list[j]);
                     matches++;
                     }
                  }
               fprintf(stderr, "}\n");
               status = ~AOK;
               }
            }
            break;

         case SELECT_ARG:
            {
            Select *sel_list = (Select *)lo;
            COMPARISON c;
            int matches = 0;
            int j, p;
   
            for (j = 0; sel_list[j].string; j++)
               {
                     // if (sel_list[j].flags & PARSER_NOHELP)
                     //    continue;
                     if ((sel_list[j].flags & PARSER_HIDDEN)
                      && (session->session_unlocked == 0) && (debug_level == 0))
                        continue;
                     if ((sel_list[j].flags & PARSER_HIDDEN_CCFG)
                      && (session->session_config == 0))
                        continue;
                     if (sel_list[j].flags & PARSER_DISABLED)
                        continue;
               c = abbr_compare(parsed_value, sel_list[j].string);
               if (c != NO_MATCH)
                  {
                  value = sel_list[j].string;
                  if (c == EXACT)
                     {
                     matches = 1;
                     break;
                     }
                  else
                     matches++;
                  }
               }
            if (matches == 0)
               {
               fprintf(stderr, " Value %s not valid for %s\n",
               parsed_value, cmd_list[cmd_index].arg[arg_index].param);
               fprintf(stderr, "  Valid values are {");
               p = 0;
               for (j = 0; sel_list[j].string; j++)
                  {
                     if (sel_list[j].flags & PARSER_NOHELP)
                        continue;
                     if ((sel_list[j].flags & PARSER_HIDDEN)
                      && (session->session_unlocked == 0) && (debug_level == 0))
                        continue;
                     if ((sel_list[j].flags & PARSER_HIDDEN_CCFG)
                      && (session->session_config == 0))
                        continue;
                     if (sel_list[j].flags & PARSER_DISABLED)
                        continue;
                  if (p) fprintf(stderr, "|");
                     fprintf(stderr, "%s", sel_list[j].string);
                     p++;
                  }
               fprintf(stderr, "}\n");
               status = ~AOK;
               }
            else if (matches > 1)
               {
               fprintf(stderr, "  Value %s for %s is ambiguous, matches {",
                       parsed_value, cmd_list[cmd_index].arg[arg_index].param);
               matches = 0;
               for (j = 0; sel_list[j].string; j++)
                  {
                     if (sel_list[j].flags & PARSER_NOHELP)
                        continue;
                     if ((sel_list[j].flags & PARSER_HIDDEN)
                      && (session->session_unlocked == 0) && (debug_level == 0))
                        continue;
                     if ((sel_list[j].flags & PARSER_HIDDEN_CCFG)
                      && (session->session_config == 0))
                        continue;
                     if (sel_list[j].flags & PARSER_DISABLED)
                        continue;
                  c = abbr_compare(parsed_value, sel_list[j].string);
                  if ( c != NO_MATCH )
                     {
                     if (matches)
                        fprintf(stderr, ", ");
                     fprintf(stderr, "%s", sel_list[j].string);
                     matches++;
                     }
                  }
               fprintf(stderr, "}\n");
               status = ~AOK;
               }
            }
            break;

         case BITS_ARG:
            {
                uint32      result;
                cmd_ret_t   idsw;

                idsw = process_bitflags(parsed_value, &result,
                                    cmd_list[cmd_index].arg[arg_index].param,
                                    (const Select *)lo, session);
                if (idsw == CMD_SUCCESS) {
                    value = parsed_value;
                } else {
                    fprintf(stderr, "Bad bitflag argument value %s\n",
                                    cmd_list[cmd_index].arg[arg_index].param);
                    value = NULL;
                    status = ~AOK;
                }
            }
            break;

         case IPADDR_ARG:
             if (missing_value)
                fprintf(stderr, "Value is an IP address\n");
             else
                value = parsed_value;
             break;
         case ALIAS_ARG:
         default:
            assert(0);
         }
   
         if (param != 0 && value != 0)
            p->add_value(param, value);
    }
    // status is AOK unless we found an error

    if (status != AOK)
    {
      delete p;
      return 0;
    }
    else
      return p;
}

const Arg parser_unlock_args[] = {
   {"debug-mode",           STRING,  0 },
   {"enable-configuration", STRING,  0 },
   {"eeprom",               STRING,  PARSER_NOHELP },
   {"compute",              STRING,  PARSER_NOHELP },
   {0,                      {UNDEF_ARG} }
};

int parser_keyhash(const char key[])
{
   int i;
   uint32 keyval;

   keyval = 0;

   for (i = 0; key[i]; i++)
   {
      keyval = (keyval << 3) ^ key[i];
   }
   return (int)(((keyval & 0x0000FFFF) + (keyval >> 16)) & 0xFFFF);
}

// Help on commands
const Arg parser_help_args[] =
{
  { "command", STRING,                0 },
  { "full",    {BOOL_ARG, 0, 1},      PARSER_NOHELP },
  { NULL,      {UNDEF_ARG}              }
};

cmd_ret_t
parser_help(
   Argument_List * a,
   ucio_session* session)
{
   boolean fullmode = parse_bool(a->get_value("full"), false);
   return parse_list_matching_commands(a->get_value("command"),
                                current_command_table, session, fullmode);
}

char *
parser_get_value(Argument_List *a, char *name)
{
    if (a == NULL)
        return NULL;
    else
        return (char *)a->get_value(name);
}

boolean
parser_empty(Argument_List *a)
{
    if (a == NULL)
        return true;
    else
        return a->empty();
}

#if 0
cmd_ret_t
parser_history(const Argument_List * const, ucio_session *session)
{
    lineedit_display_history(session);
    return CMD_SUCCESS;
}

cmd_ret_t
parser_unlock(const Argument_List * const a, ucio_session* session)
{
    const char *key;

    key = a->get_value("debug-mode");
    if ((key) && strlen(key))
    {
      if (parser_keyhash(key) == parser_key)
         {
         session->session_unlocked = true;
         fprintf(stderr, "Debug-mode commands UNLOCKED\n");
         lineedit_delete_last_line(session);
         }
      else
         {
         session->session_unlocked = false;
         fprintf(stderr, "Debug-mode commands LOCKED\n");
         }
    }

    key = a->get_value("enable-configuration");
    if ((key) && strlen(key))
    {
        uint16 keyval, password;
        keyval = parser_keyhash(key);
        password = my_config_pwd_hash;
        if (password == 0)
        {// unit was powered-up in enable-configuration mode
         cerr << "Unit has no password" << endl;
         session->session_config = true;
        } else
        {
            if (keyval == password)
            {
                session->session_config = true;
                cerr << "Configuration commands UNLOCKED" << endl;
                lineedit_delete_last_line(session);
            } else
            {
                session->session_config = false;
                cerr << "Configuration commands LOCKED" << endl;
            }
        }
    }

    key = a->get_value("eeprom");
    if ((key) && strlen(key) && session->session_unlocked)
    {
        int keyval;
        keyval = parser_keyhash(key);
        if (eeprom_unlock(keyval))
        {
            cerr << "EEPROM commands UNLOCKED" << endl;
            lineedit_delete_last_line(session);
        } else {
            cerr << "EEPROM commands LOCKED" << endl;
        }
    }

    key = a->get_value("compute");
    if ((key) && strlen(key))
    {
      int keyval;
      char hexval[8];
      keyval = parser_keyhash(key);
      sprintf(hexval, "%04x", keyval);
      cerr << "Hash value of " << key << " is " << keyval
         << " (hex " << hexval << ") "<< endl;
    }
    return CMD_SUCCESS;
}

cmd_ret_t
lock_privileged_cmds(ucio_session *session)
{   
   if (session->session_unlocked == true)
   {
      session->session_unlocked = false;
      ucio_fprintf(session->session_outfile, "Debug-mode commands LOCKED\n");
   }
   eeprom_unlock(0);
   return CMD_SUCCESS;
}

/**************************************************************************
void lock_enable_configuration(ucio_session *session)

   Without proper authorization the user cannot change the configuration.
   To access the privileged state, the user must issue the following command:
     (1) unlock enable-configuration=<password>
   While the user is in the privileged state, the command prompt is changed
   from "nodename>" to "nodename#".

   To get back to the unprivileged state, the user must issue one of the
   following two commands:
      (1) unlock enable-configuration=no
      (2) lock
   The unit will also leave the privileged state if more than ten minutes had
   passed since the last command.
***************************************************************************/

cmd_ret_t
lock_enable_configuration(ucio_session *session)
{
    if (session->session_config)
    {
        session->session_config = false;
        fprintf(session->session_outfile,
            "Configuration commands LOCKED\n");
    }
    return CMD_SUCCESS;
}

cmd_ret_t
parser_lock_all(const Argument_List * const, ucio_session *s)
{
    if (s->session_unlocked == true)
    {
      s->session_unlocked = false;
      fprintf(s->session_outfile, "Debug-mode commands LOCKED\n");
    }

    if (my_config_pwd_hash != 0)   
        lock_enable_configuration(s);

    eeprom_unlock(0);
    return CMD_SUCCESS;
}


const Arg change_password_args[] = {
   {"enable-configuration", STRING, 0},
   0, UNDEF_ARG
};

/**************************************************************************
cmd_ret_t change_password(const Argument_List * const a, ostream &stream)

   To change the password, the user issues the command:
      'change-password enable-configuration=<password>'

   Both successful "unlock" command and "change-password" command will clear
   the last command entered into the command history table (so that the 
   command recall feature will not reveal the password).
***************************************************************************/
cmd_ret_t
change_password(const Argument_List * const a, ucio_session* session)
{
    const char *key;

    key = a->get_value("enable-configuration");
    if ((key) && strlen(key))
    {
      my_config_pwd_hash = parser_keyhash(key);
      lineedit_delete_last_line(session);
    }
    return CMD_SUCCESS;
}

// Once we are running, the "enable-configuration" password is located
//    in coredata.c variable (uint16) my_config_pwd_hash
// The hash function is such that a blank string (no password)
//    will generate a zero value for the hash.
// The value in coredata is defaulted to zero, so that the bootrom
//    application will come up enabled; the VINE application will
//    copy the hash value from the configuration structure in EEPROM
//    into coredata whenever we load a new configuration, and will
//    also retrieve the value from there before saving a configuration
//    to EEPROM


// Protecting Configuration Data:
// The unit will leave the privileged state if more than 600 seconds
// had passed since the last command.
// CLOCKHZ is the number of clock interrupts per second
void
enable_config_check_timeout(void)
{
   if (my_config_pwd_hash == 0)
      return;

   for (int i = 0; i < MAX_UCIO_SESSIONS; i++)
      if (session_table[i].session_unlocked)
      {
         if (((rtos_get_time()
               - session_table[i].timestamp) / CLOCKHZ)
            > 600)
         {
            lock_privileged_cmds(&session_table[i]);
            lock_enable_configuration(&session_table[i]);
            fprintf(session_table[i].session_outfile,
                         "\n****** Command Session Timeout *****\n");
         }
      }
}
#endif

// ----- parser_disable_command(cmdname) --------------------------------------
// Set the PARSER_DISABLED flag on a command

cmd_ret_t
parser_disable_command(const char *cmdname)
{
    Command        *cmdp;

    for (cmdp = current_command_table; cmdp->name; cmdp++)
    {
        if (cmdp->flags & PARSER_COMMENT) continue;
        if (strcmp(cmdp->name, cmdname) == 0)
        {
            cmdp->flags |= PARSER_DISABLED;
            return CMD_SUCCESS;
        }
    }
    return CMD_NOT_FOUND;
}

// ----- parser_enable_command(cmdname) --------------------------------------
// Remove the PARSER_DISABLED flag from a command

cmd_ret_t
parser_enable_command(const char *cmdname)
{
    Command        *cmdp;

    for (cmdp = current_command_table; cmdp->name; cmdp++)
    {
        if (cmdp->flags & PARSER_COMMENT) continue;
        if (strcmp(cmdp->name, cmdname) == 0)
        {
            cmdp->flags &= ~PARSER_DISABLED;
            return CMD_SUCCESS;
        }
    }
    return CMD_NOT_FOUND;
}

// ----- parser_enable_argument(cmdname, argname) ----------------------------
// Remove the PARSER_DISABLED flag from a command argument

cmd_ret_t
parser_enable_argument(const char *cmdname, const char *argname)
{
    Command        *cmdp;
    Arg            *argp;

    for (cmdp = current_command_table; cmdp->name; cmdp++)
    {
        if (cmdp->flags & PARSER_COMMENT) continue;

        if (strncmp(cmdp->name, cmdname, strlen(cmdname)) == 0)
        {
            break;
        }
    }

    if (cmdp->name == NULL) return CMD_NOT_FOUND;

    for (argp = (Arg *)cmdp->arg; argp->param; argp++)
    {
        if (strcmp(argp->param, argname) == 0) break;
    }

    if (argp->param == NULL) return CMD_BAD_ARG_NAME;

    argp->flags &= ~PARSER_DISABLED;
    return CMD_SUCCESS;
}

cmd_ret_t
parser_disable_argument(const char *cmdname, const char *argname)
{
    Command        *cmdp;
    Arg            *argp;

    for (cmdp = current_command_table; cmdp->name; cmdp++)
    {
        if (cmdp->flags & PARSER_COMMENT) continue;

        if (strncmp(cmdp->name, cmdname, strlen(cmdname)) == 0)
        {
            break;
        }
    }

    if (cmdp->name == NULL) return CMD_NOT_FOUND;

    for (argp = (Arg *)cmdp->arg; argp->param; argp++)
    {
        if (strcmp(argp->param, argname) == 0) break;
    }

    if (argp->param == NULL) return CMD_BAD_ARG_NAME;

    argp->flags |= PARSER_DISABLED;
    return CMD_SUCCESS;
}

// ----- parser_enable_arg_value(cmdname, argname, valname) -------------------
// Remove the PARSER_DISABLED flag from a SELECT value for a command argument

cmd_ret_t
parser_enable_arg_value(const char *cmdname, const char *argname,
                                                           const char *valname)
{
    Command        *cmdp;
    Arg            *argp;
    Select         *selp;

    for (cmdp = current_command_table; cmdp->name; cmdp++)
    {
        if (cmdp->flags & PARSER_COMMENT) continue;
        if (strcmp(cmdp->name, cmdname) == 0)
        {
            break;
        }
    }

    if (cmdp->name == NULL) return CMD_NOT_FOUND;

    for (argp = (Arg *)cmdp->arg; argp->param; argp++)
    {
        if (strcmp(argp->param, argname) == 0) break;
    }

    if (argp->param == NULL) return CMD_BAD_ARG_NAME;
    if (argp->value.kind != SELECT_ARG) return CMD_BAD_ARG_VALUE;

    for (selp = (Select *)argp->value.lo; selp->string; selp++)
    {
        if (strcmp(selp->string, valname) == 0)
        {
            break;
        }
    }

    if (selp->string == NULL) return CMD_BAD_ARG_VALUE;

    selp->flags &= ~PARSER_DISABLED;
    return CMD_SUCCESS;
}

cmd_ret_t
parser_disable_arg_value(const char *cmdname, const char *argname,
                                                           const char *valname)
{
    Command        *cmdp;
    Arg            *argp;
    Select         *selp;

    for (cmdp = current_command_table; cmdp->name; cmdp++)
    {
        if (cmdp->flags & PARSER_COMMENT) continue;
        if (strcmp(cmdp->name, cmdname) == 0)
        {
            break;
        }
    }

    if (cmdp->name == NULL) return CMD_NOT_FOUND;

    for (argp = (Arg *)cmdp->arg; argp->param; argp++)
    {
        if (strcmp(argp->param, argname) == 0) break;
    }

    if (argp->param == NULL) return CMD_BAD_ARG_NAME;
    if (argp->value.kind != SELECT_ARG) return CMD_BAD_ARG_VALUE;

    for (selp = (Select *)argp->value.lo; selp->string; selp++)
    {
        if (strcmp(selp->string, valname) == 0)
        {
            break;
        }
    }

    if (selp->string == NULL) return CMD_BAD_ARG_VALUE;

    selp->flags |= PARSER_DISABLED;
    return CMD_SUCCESS;
}

// ----- parser_enable_selection(sel_tab, selname) ---------------------------

cmd_ret_t
parser_enable_selection(const Select *sel_tab, const char *sel_name)
{
    Select *selp;
    for (selp = (Select *)sel_tab; selp->string; selp++)
    {
        if (strcmp(selp->string, sel_name) == 0) break;
    }
    if (selp->string == NULL) return CMD_BAD_ARG_VALUE;
    selp->flags &= ~PARSER_DISABLED;
    return CMD_SUCCESS;
}

// ----- parser_enable_cmd() -------------------------------------------------

const Arg parser_enable_args[] = {
   { "command",           STRING,   0 },
   { "argument",          STRING,   0 },
   { 0,                   {UNDEF_ARG} }
};

cmd_ret_t
parser_enable_cmd(const Argument_List * const a, ucio_session* session)
{
    cmd_ret_t   retval;
    const char *c, *p;

    c = a->get_value("command");
    if (!c) return CMD_ARG_MISSING;

    p = a->get_value("argument");

    if (p == NULL)
        retval = parser_enable_command(c);
    else
        retval = parser_enable_argument(c, p);
    
    return retval;
}

// ----- process_bitflags() ---------------------------------------------------
cmd_ret_t
process_bitflags(const char *parsed_value, uint32 *result_mask,
                 const char *arg_name,
                 const Select *sel_list, ucio_session *session)
{
#define BF_DEBUG 0
    // parsed_value: char * to string supplied by user
    //               provided to us by code above that sorted
    //               out which argument this is.
    //               The form of the argument is
    //                "keywd1,keywd2,keywd3"
    //                or "keywd1+keywd2+keywd3"
    COMPARISON  c;
    int         matches = 0;
    int         j, m, p;
    char       *cur_kw;
    uint32      result = 0;
    char       *cp = (char *)parsed_value;
    int         numbits = 0;
    cmd_ret_t   retval = CMD_SUCCESS;
    FILE       *out;

    if (session) out = session->session_outfile;

    if ((cp == NULL)
      || strlen(cp) == 0)
    {
        *result_mask = 0;
        return CMD_SUCCESS;
    }

    if (strchr("abcdefghijklmnopqrstuvwxyz", *cp) == NULL)
    {
        if (session)
            fprintf(out, "  Bitflag %s (%s) starts badly\n", arg_name, cp);
        return CMD_BAD_ARG_VALUE;
    }
    numbits++;
    cur_kw = cp;

    do {
        if ( (strchr("abcdefghijklmnopqrstuvwxyz0123456789-", *cp))
          && (*(cp+1) != 0) )
            continue;

        if (*cp == ',' || *cp == '+' || *(cp+1) == 0)
        {
            char kw_buf[16];
            size_t kw_len;

            if ( (*(cp + 1) != 0)
              && (strchr("abcdefghijklmnopqrstuvwxyz", *(cp + 1)) == NULL) )
            {
                if (!session) return CMD_BAD_ARG_VALUE;

                fprintf(out,
                    "  Segment %d of Bitflag %s (%s) ends badly\n",
                    numbits, arg_name, cur_kw);
                retval = CMD_BAD_ARG_VALUE;
                continue;
            }

            kw_len = cp - cur_kw;
            if ((*cp != ',') && (*cp != '+'))
                kw_len++;
            if (kw_len > 15)
                kw_len = 15;
            strncpy(kw_buf, cur_kw, kw_len);
            kw_buf[kw_len] = 0;
            matches        = 0;

            if (session && BF_DEBUG)
                fprintf(out, "Checking keyword \"%s\"\n", kw_buf);

            for (j = 0; sel_list[j].string; j++)
            {
                if (sel_list[j].flags & PARSER_NOHELP)
                    continue;
                if ((sel_list[j].flags & PARSER_HIDDEN)
                 && (session->session_unlocked == 0) && (debug_level == 0))
                    continue;
                if ((sel_list[j].flags & PARSER_HIDDEN_CCFG)
                    && (session->session_config == 0))
                    continue;
                if (sel_list[j].flags & PARSER_DISABLED)
                    continue;
                c = abbr_compare(kw_buf, sel_list[j].string);
                if (c != NO_MATCH)
                {
                    // value = sel_list[j].string;
                    m = j;

                    if (c == EXACT)
                    {
                        matches = 1;
                        break;
                    }
                    else
                        matches++;
                }
            }
            if (matches == 0)
            {
                if (!session) return CMD_BAD_ARG_VALUE;

                fprintf(out, "  Value %s not valid for %s\n",
                             kw_buf, arg_name);
                fprintf(out, "  Valid values are {");
                p = 0;
                for (j = 0; sel_list[j].string; j++)
                {
                    if (sel_list[j].flags & PARSER_NOHELP)
                        continue;
                    if ((sel_list[j].flags & PARSER_HIDDEN)
                     && (session->session_unlocked == 0) && (debug_level == 0))
                        continue;
                    if ((sel_list[j].flags & PARSER_HIDDEN_CCFG)
                        && (session->session_config == 0))
                        continue;
                    if (sel_list[j].flags & PARSER_DISABLED)
                        continue;
                    if (p)
                        fprintf(out, "|");
                    fprintf(out, sel_list[j].string);
                    p++;
                }
                        fprintf(out, "}\n");
                retval = CMD_BAD_ARG_VALUE;
            }
            else if (matches > 1)
            {
                if (!session) return CMD_BAD_ARG_VALUE;

                fprintf(out,
                             "  Value %s for %s is ambiguous, matches {",
                             kw_buf, arg_name);
                matches = 0;
                for (j = 0; sel_list[j].string; j++)
                {
                    if (sel_list[j].flags & PARSER_NOHELP)
                        continue;
                    if ((sel_list[j].flags & PARSER_HIDDEN)
                     && (session->session_unlocked == 0) && (debug_level == 0))
                        continue;
                    if ((sel_list[j].flags & PARSER_HIDDEN_CCFG)
                        && (session->session_config == 0))
                        continue;
                    if (sel_list[j].flags & PARSER_DISABLED)
                        continue;
                    c = abbr_compare(kw_buf, sel_list[j].string);
                    if (c != NO_MATCH)
                    {
                        if (matches)
                            fprintf(out, ", ");
                        fprintf(out, sel_list[j].string);
                        matches++;
                    }
                }
                fprintf(out, "}\n");
                result = 0;
                retval = CMD_BAD_ARG_VALUE;
            }
            else {
                if (session && BF_DEBUG) {
                        fprintf(out, " Added %08X (%s)\n",
                                     (1 << m), sel_list[m].string);
                }
                result |= (1 << m);
            }
            numbits++;
            cur_kw = cp + 1;
        }
    } while (*++cp);

    if (session && BF_DEBUG) {
        fprintf(out, " Bitmask returning result %08X\n", result);
    }
    *result_mask = result;
    return retval;
}
char *
parser_format_bitflags(int value, Select sel_tab[])
{
    static char outbuf[80];
    int         i;
    int         bytes_so_far;

    outbuf[0] = 0;
    for (i = 0; sel_tab[i].string; i++) {
        bytes_so_far = strlen(outbuf);
        if (value & (1 << i)) {
            if (bytes_so_far)
                outbuf[bytes_so_far++] = ',';
            strncpy(outbuf+bytes_so_far, sel_tab[i].string,
                        80 - bytes_so_far - 2);
        }
    }
    return outbuf;
}
// End
