"Fossies" - the Fresh Open Source Software Archive

Member "coda-6.9.5/lib-src/base/parser.c" (17 Dec 2007, 13599 Bytes) of package /linux/misc/old/coda-6.9.5.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "parser.c" see the Fossies "Dox" file reference documentation.

    1 /* BLURB lgpl
    2 
    3                            Coda File System
    4                               Release 6
    5 
    6           Copyright (c) 1987-2003 Carnegie Mellon University
    7                   Additional copyrights listed below
    8 
    9 This  code  is  distributed "AS IS" without warranty of any kind under
   10 the  terms of the  GNU  Library General Public Licence  Version 2,  as
   11 shown in the file LICENSE. The technical and financial contributors to
   12 Coda are listed in the file CREDITS.
   13 
   14                         Additional copyrights
   15                            none currently
   16 
   17 #*/
   18 
   19 #ifdef __cplusplus
   20 extern "C" {
   21 #endif /* __cplusplus */
   22 
   23 #ifdef HAVE_CONFIG_H
   24 #include <config.h>
   25 #endif
   26 
   27 #include <stdio.h>
   28 #include <stdlib.h>
   29 #include <ctype.h>
   30 #include "coda_string.h"
   31 #include <stddef.h>
   32 #include <sys/param.h>
   33 #include "coda_assert.h"
   34 
   35 #include <readline/readline.h>
   36 #include <readline/history.h>
   37 
   38 #ifdef __cplusplus
   39 }
   40 #endif /* __cplusplus */
   41 
   42 #ifndef HAVE_RL_COMPLETION_MATCHES
   43 /* compatibility for readline libs < v4.2 */
   44 #define rl_completion_matches completion_matches
   45 #endif
   46 
   47 #include "parser.h"
   48 #define CMD_COMPLETE 0
   49 #define CMD_INCOMPLETE 1
   50 #define CMD_NONE 2
   51 #define CMD_AMBIG 3
   52 
   53 static command_t * top_level;      /* Top level of commands, initialized by
   54                     * InitParser                */
   55 static command_t * match_tbl;      /* Command completion against this table */
   56 static char * parser_prompt = NULL;/* Parser prompt, set by InitParser      */
   57 static int done;           /* Set to 1 if user types exit or quit   */
   58 
   59 
   60 /* static functions */
   61 static char *skipwhitespace(char *s);
   62 static char *skiptowhitespace(char *s);
   63 static command_t *find_cmd(char *name, command_t cmds[], char **next);
   64 static int process(char *s, char **next, command_t *lookup, command_t **result, char **prev);
   65 static char *command_generator(const char *text, int state);
   66 static char **command_completion(const char *text, int start, int end);
   67 static void print_commands(const char *str, command_t *table);
   68 
   69 #if 1
   70 static char * skipwhitespace(char * s)
   71 {
   72     char * t;
   73     int    len;
   74 
   75     len = (int)strlen(s);
   76 
   77     for (t = s; t <= s + len && isspace((int)*t); t++);
   78     return(t);
   79 }
   80 
   81 static char * skiptowhitespace(char * s) 
   82 {
   83     char * t;
   84     
   85     for (t = s; *t && !isspace((int)*t); t++);
   86     return(t);
   87 }
   88 
   89 #else
   90 
   91 char * skipwhitespace(char *a) {
   92    while (*a != '\0' && isspace(*a))
   93     a++;
   94    return a;
   95 }
   96 
   97 char * skiptowhitespace(char *a) {
   98    while (*a != '\0' && !isspace(*a))
   99     a++;
  100    return a;
  101 }
  102 #endif
  103 
  104 int line2args(char *line, char **argv, int maxargs)
  105 {
  106     char *arg;
  107     int i = 0; 
  108     
  109     arg = strtok(line, " \t");
  110     if ( arg ) {
  111     argv[i] = arg;
  112     i++;
  113     } else 
  114     return 0;
  115 
  116     while( (arg = strtok(NULL, " \t")) && (i <= maxargs)) {
  117     argv[i] = arg;
  118     i++;
  119     }
  120     return i;
  121 }
  122 
  123 /* find a command -- return it if unique otherwise print alternatives */
  124     
  125 static argcmd_t *Parser_findargcmd(char *name, argcmd_t cmds[])
  126 {
  127     argcmd_t *cmd;
  128     int i;
  129 
  130     for (i = 0; cmds[i].ac_name; i++) {
  131         cmd = &cmds[i];
  132         if (strcmp(name, cmd->ac_name) == 0) 
  133             return cmd;
  134     }
  135     return NULL;
  136 }
  137 
  138 int Parser_execarg(int argc, char **argv, argcmd_t cmds[])
  139 {
  140     argcmd_t *cmd;
  141     int i;
  142 
  143         cmd = Parser_findargcmd(argv[0], cmds);
  144     if ( cmd )
  145         return (cmd->ac_func)(argc, argv);
  146 
  147     printf("Try interactive use without arguments or use one of: ");
  148     for (i=0 ; cmds[i].ac_name ; i++) {
  149         cmd = &cmds[i];
  150         printf("\"%s\" ", cmd->ac_name);
  151     }
  152     printf("as argument.\n");
  153 
  154     return -1;
  155 }
  156 
  157 /* returns the command_t * (NULL if not found) corresponding to a
  158    _partial_ match with the first token in name.  It sets *next to
  159    point to the following token. Does not modify *name. */
  160 static command_t * find_cmd(char * name, command_t cmds[], char ** next) 
  161 {
  162     int    i, len;
  163     
  164     if (!cmds || !name ) 
  165         return NULL;
  166     
  167     /* This sets name to point to the first non-white space character,
  168     and next to the first whitespace after name, len to the length: do
  169     this with strtok*/
  170     name = skipwhitespace(name);
  171     *next = skiptowhitespace(name);
  172     len = *next - name;
  173     if (len == 0) 
  174     return NULL;
  175 
  176     for (i = 0; cmds[i].name; i++) {
  177     if (strncasecmp(name, cmds[i].name, len) == 0) {
  178         *next = skipwhitespace(*next);
  179         return(&cmds[i]);
  180     }
  181     }
  182     return NULL;
  183 }
  184 
  185 /* Recursively process a command line string s and find the command
  186    corresponding to it. This can be ambiguous, full, incomplete,
  187    non-existent. */
  188 static int process(char *s, char ** next, command_t *lookup,
  189            command_t **result, char **prev)
  190 {
  191     *result = find_cmd(s, lookup, next);
  192     *prev = s; 
  193 
  194     /* non existent */
  195     if ( ! *result ) 
  196     return CMD_NONE;
  197 
  198     /* found entry: is it ambigous, i.e. not exact command name and
  199        more than one command in the list matches.  Note that find_cmd
  200        points to the first ambiguous entry */
  201     if ( strncasecmp(s, (*result)->name, strlen((*result)->name)) &&
  202      find_cmd(s, (*result) + 1, next)) 
  203     return CMD_AMBIG;
  204 
  205     /* found a unique command: component or full? */
  206     if ( (*result)->func ) {
  207     return CMD_COMPLETE;
  208     } else {
  209     if ( *next == '\0' ) {
  210         return CMD_INCOMPLETE;
  211     } else {
  212         return process(*next, next, (*result)->sub_cmd, result, prev);
  213     }
  214     }
  215 }
  216 
  217 static char *command_generator(const char *text, int state)
  218 {
  219     static int index, len;
  220     const char *name;
  221 
  222     /* Do we have a match table? */
  223     if (!match_tbl)
  224     return NULL;
  225 
  226     /* If this is the first time called on this word, state is 0 */
  227     if (!state) {
  228     index = 0;
  229     len = (int)strlen(text);
  230     }
  231 
  232     /* Return the next name in the command list that paritally matches test */
  233     while ((name = (match_tbl + index)->name) != NULL)
  234     {
  235     index++;
  236 
  237     if (strncasecmp(name, text, len) == 0)
  238         return strdup(name);
  239     }
  240 
  241     /* No more matches */
  242     return NULL;
  243 }
  244 
  245 /* probably called by readline */
  246 static char **command_completion(const char *text, int start, int end)
  247 {
  248     command_t *table;
  249     char *pos;
  250 
  251     match_tbl = top_level;
  252     pos = rl_line_buffer;
  253 
  254     while((table = find_cmd(pos, match_tbl, &pos)) != NULL)
  255     if (*(pos - 1) == ' ')
  256         match_tbl = table->sub_cmd;
  257 
  258     return rl_completion_matches(text, command_generator);
  259 }
  260 
  261 /* take a string and execute the function or print help */
  262 void execute_line(char * line) 
  263 {
  264     command_t   *cmd, *ambig;
  265     char *prev;
  266     char *next, *tmp;
  267     char *argv[MAXARGS];
  268     int  i;
  269 
  270     switch( process(line, &next, top_level, &cmd, &prev) ) {
  271     case CMD_AMBIG:
  272     fprintf(stderr, "Ambiguous command \'%s\'\nOptions: ", line);
  273     while( (ambig = find_cmd(prev, cmd, &tmp)) ) {
  274         fprintf(stderr, "%s ", ambig->name);
  275         cmd = ambig + 1;
  276     }
  277     fprintf(stderr, "\n");
  278     break;
  279     case CMD_NONE:
  280     fprintf(stderr, "No such command, type help\n");
  281     break;
  282     case CMD_INCOMPLETE:
  283     fprintf(stderr,
  284         "'%s' incomplete command.  Use '%s x' where x is one of:\n",
  285         line, line);
  286     fprintf(stderr, "\t");
  287     for (i = 0; cmd->sub_cmd[i].name; i++) {
  288         fprintf(stderr, "%s ", cmd->sub_cmd[i].name);
  289     }
  290     fprintf(stderr, "\n");
  291     break;
  292     case CMD_COMPLETE:
  293     i = line2args(line, argv, MAXARGS);
  294     (cmd->func)(i, argv);
  295     break;
  296     }
  297     
  298     return;
  299 }
  300 
  301 /* this is the command execution machine */
  302 void Parser_commands(void)
  303 {
  304     char *line,
  305      *s;
  306 
  307     using_history();
  308     stifle_history(HISTORY);
  309 
  310     rl_attempted_completion_function = command_completion;
  311     rl_completion_entry_function = &command_generator;
  312 
  313     while(!done) {
  314     line = readline(parser_prompt);
  315 
  316     if (!line) break;
  317 
  318     s = skipwhitespace(line);
  319 
  320     if (*s) {
  321         add_history(s);
  322         execute_line(s);
  323     }
  324 
  325     free(line);
  326     }
  327 }
  328 
  329 
  330 /* sets the parser prompt */
  331 void Parser_init(const char *prompt, command_t * cmds)
  332 {
  333     done = 0;
  334     top_level = cmds;
  335     if (parser_prompt) free(parser_prompt);
  336     parser_prompt = strdup(prompt);
  337 }
  338 
  339 /* frees the parser prompt */
  340 void Parser_exit(int argc, char *argv[])
  341 {
  342     done = 1;
  343     free(parser_prompt);
  344     parser_prompt = NULL;
  345 }
  346 
  347 /* convert a string to an unsigned integer */
  348 int Parser_uint(char *s, unsigned int *val)
  349 {
  350     int ret;
  351 
  352     if (*s != '0')
  353     ret = sscanf(s, "%u", val);
  354     else if (*(s+1) != 'x')
  355     ret = sscanf(s, "%o", val);
  356     else {
  357     s++;
  358     ret = sscanf(++s, "%x", val);
  359     }
  360 
  361     return(ret);
  362 }
  363 
  364 
  365     
  366 void Parser_qhelp(int argc, char *argv[]) {
  367 
  368     printf("Available commands are:\n");
  369     
  370     print_commands(NULL, top_level);
  371 }
  372 
  373 void Parser_help(int argc, char **argv) 
  374 {
  375     char line[1024];
  376     char *next, *prev, *tmp;
  377     command_t *result, *ambig;
  378     int i;
  379 
  380     if ( argc == 1 ) {
  381     Parser_qhelp(argc, argv);
  382     return;
  383     }
  384 
  385     line[0]='\0';
  386     for ( i = 1 ;  i < argc ; i++ ) {
  387     strcat(line, argv[i]);
  388     }
  389 
  390     switch ( process(line, &next, top_level, &result, &prev) ) {
  391     case CMD_COMPLETE:
  392     fprintf(stderr, "%s: %s\n",line, result->help);
  393     break;
  394     case CMD_NONE:
  395     fprintf(stderr, "%s: Unknown command.\n", line);
  396     break;
  397     case CMD_INCOMPLETE:
  398     fprintf(stderr,
  399         "'%s' incomplete command.  Use '%s x' where x is one of:\n",
  400         line, line);
  401     fprintf(stderr, "\t");
  402     for (i = 0; result->sub_cmd[i].name; i++) {
  403         fprintf(stderr, "%s ", result->sub_cmd[i].name);
  404     }
  405     fprintf(stderr, "\n");
  406     break;
  407     case CMD_AMBIG:
  408     fprintf(stderr, "Ambiguous command \'%s\'\nOptions: ", line);
  409     while( (ambig = find_cmd(prev, result, &tmp)) ) {
  410         fprintf(stderr, "%s ", ambig->name);
  411         result = ambig + 1;
  412     }
  413     fprintf(stderr, "\n");
  414     break;
  415     }
  416     return;
  417 }  
  418 
  419 /*************************************************************************
  420  * COMMANDS                              *
  421  *************************************************************************/ 
  422 
  423 static void print_commands(const char *str, command_t *table)
  424 {
  425     command_t *cmds;
  426     char buf[80];
  427 
  428     for (cmds = table; cmds->name; cmds++) {
  429     if (cmds->func) {
  430         if (str) printf("\t%s %s\n", str, cmds->name);
  431         else printf("\t%s\n", cmds->name);
  432     }
  433     if (cmds->sub_cmd) {
  434         if (str) {
  435         sprintf(buf, "%s %s", str, cmds->name);
  436         print_commands(buf, cmds->sub_cmd);
  437         } else {
  438         print_commands(cmds->name, cmds->sub_cmd);
  439         }
  440     }
  441     }
  442 }
  443 
  444 char *Parser_getstr(const char *prompt, const char *deft, char *res, 
  445             size_t len)
  446 {
  447     char *line = NULL;
  448     int size = strlen(prompt) + strlen(deft) + 8;
  449     char *theprompt;
  450     theprompt = malloc(size);
  451     CODA_ASSERT(theprompt);
  452 
  453     sprintf(theprompt, "%s [%s]: ", prompt, deft);
  454 
  455     line  = readline(theprompt);
  456     free(theprompt);
  457 
  458     if ( line == NULL || *line == '\0' ) {
  459     strncpy(res, deft, len);
  460     } else {
  461     strncpy(res, line, len);
  462     }
  463 
  464     if ( line ) {
  465     free(line);
  466     return res;
  467     } else {
  468     return NULL;
  469     }
  470 }
  471 
  472 /* get integer from prompt, loop forever to get it */
  473 int Parser_getint(const char *prompt, long min, long max, long deft, int base)
  474 {
  475     int rc;
  476     long result;
  477     char *line;
  478     int size = strlen(prompt) + 40;
  479     char *theprompt = malloc(size);
  480     CODA_ASSERT(theprompt);
  481     sprintf(theprompt,"%s [%ld, (0x%lx)]: ", prompt, deft, deft);
  482 
  483     fflush(stdout);
  484 
  485     do {
  486     line = NULL;
  487     line = readline(theprompt);
  488     if ( !line ) {
  489         fprintf(stdout, "Please enter an integer.\n");
  490         fflush(stdout);
  491         continue;
  492     }
  493     if ( *line == '\0' ) {
  494         free(line);
  495         result =  deft;
  496         break;
  497     }
  498     rc = Parser_arg2int(line, &result, base);
  499     free(line);
  500     if ( rc != 0 ) {
  501         fprintf(stdout, "Invalid string.\n");
  502         fflush(stdout);
  503     } else if ( result > max || result < min ) {
  504         fprintf(stdout, "Error: response must lie between %ld and %ld.\n",
  505             min, max);
  506         fflush(stdout);
  507     } else {
  508         break;
  509     }
  510     } while ( 1 ) ;
  511 
  512     if (theprompt)
  513     free(theprompt);
  514     return result;
  515 
  516 }
  517 
  518 /* get boolean (starting with YyNn; loop forever */
  519 int Parser_getbool(const char *prompt, int deft)
  520 {
  521     int result = 0;
  522     char *line;
  523     int size = strlen(prompt) + 8;
  524     char *theprompt = malloc(size);
  525     CODA_ASSERT(theprompt);
  526 
  527     fflush(stdout);
  528     
  529     if ( deft != 0 && deft != 1 ) {
  530     fprintf(stderr, "Error: Parser_getbool given bad default (%d).\n",
  531         deft);
  532     CODA_ASSERT ( 0 );
  533     }
  534     sprintf(theprompt, "%s [%s]: ", prompt, (deft==0)? "N" : "Y");
  535 
  536     do {
  537     line = NULL;
  538     line = readline(theprompt);
  539     if ( line == NULL ) {
  540         result = deft;
  541         break;
  542     }
  543     if ( *line == '\0' ) {
  544         result = deft;
  545         break;
  546     }
  547     if ( *line == 'y' || *line == 'Y' ) {
  548         result = 1;
  549         break;
  550     }
  551     if ( *line == 'n' || *line == 'N' ) {
  552         result = 0;
  553         break;
  554     }
  555     if ( line ) 
  556         free(line);
  557     fprintf(stdout, "Invalid string. Must start with yY or nN\n");
  558     fflush(stdout);
  559     } while ( 1 );
  560 
  561     if ( line ) 
  562     free(line);
  563     if ( theprompt ) 
  564     free(theprompt);
  565     return result;
  566 }
  567 
  568 /* parse int out of a string or prompt for it */
  569 long Parser_intarg(const char *inp, const char *prompt, int deft,
  570           int min, int max, int base)
  571 {
  572     long result;
  573     int rc; 
  574     
  575     rc = Parser_arg2int(inp, &result, base);
  576 
  577     if ( rc == 0 ) {
  578     return result;
  579     } else {
  580     return Parser_getint(prompt, deft, min, max, base);
  581     }
  582 }
  583 
  584 /* parse int out of a string or prompt for it */
  585 char *Parser_strarg(char *inp, const char *prompt, const char *deft,
  586             char *answer, int len)
  587 {
  588     
  589     if ( inp == NULL || *inp == '\0' ) {
  590     return Parser_getstr(prompt, deft, answer, len);
  591     } else 
  592     return inp;
  593 }
  594 
  595 /* change a string into a number: return 0 on success. No invalid characters
  596    allowed. The processing of base and validity follows strtol(3)*/
  597 int Parser_arg2int(const char *inp, long *result, int base)
  598 {
  599     char *endptr;
  600 
  601     if ( (base !=0) && (base < 2 || base > 36) )
  602     return 1;
  603 
  604     *result = strtol(inp, &endptr, base);
  605 
  606     if ( *inp != '\0' && *endptr == '\0' )
  607     return 0;
  608     else 
  609     return 1;
  610 }