Whamcloud - gitweb
b=16098
[fs/lustre-release.git] / lnet / utils / parser.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * GPL HEADER START
5  *
6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 only,
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License version 2 for more details (a copy is included
16  * in the LICENSE file that accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License
19  * version 2 along with this program; If not, see [sun.com URL with a
20  * copy of GPLv2].
21  *
22  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23  * CA 95054 USA or visit www.sun.com if you need additional information or
24  * have any questions.
25  *
26  * GPL HEADER END
27  */
28 /*
29  * Copyright  2008 Sun Microsystems, Inc. All rights reserved
30  * Use is subject to license terms.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  */
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <ctype.h>
39 #include <string.h>
40 #include <stddef.h>
41 #include <unistd.h>
42 #include <sys/param.h>
43 #include <assert.h>
44 #include <lnet/api-support.h>
45
46 #include "parser.h"
47
48 static command_t * top_level;      /* Top level of commands, initialized by
49                                     * InitParser                            */
50 static char * parser_prompt = NULL;/* Parser prompt, set by InitParser      */
51 static int done;                   /* Set to 1 if user types exit or quit   */
52
53
54 /* static functions */
55 static char *skipwhitespace(char *s);
56 static char *skiptowhitespace(char *s);
57 static command_t *find_cmd(char *name, command_t cmds[], char **next);
58 static int process(char *s, char **next, command_t *lookup, command_t **result,
59                    char **prev);
60 static void print_commands(char *str, command_t *table);
61
62 static char * skipwhitespace(char * s)
63 {
64     char * t;
65     int    len;
66
67     len = (int)strlen(s);
68     for (t = s; t <= s + len && isspace(*t); t++);
69     return(t);
70 }
71
72
73 static char * skiptowhitespace(char * s)
74 {
75     char * t;
76
77     for (t = s; *t && !isspace(*t); t++);
78     return(t);
79 }
80
81 static int line2args(char *line, char **argv, int maxargs)
82 {
83     char *arg;
84     int i = 0;
85
86     arg = strtok(line, " \t");
87     if ( arg ) {
88             argv[i] = arg;
89         i++;
90     } else
91         return 0;
92
93     while( (arg = strtok(NULL, " \t")) && (i <= maxargs)) {
94         argv[i] = arg;
95         i++;
96     }
97     return i;
98 }
99
100 /* find a command -- return it if unique otherwise print alternatives */
101 static command_t *Parser_findargcmd(char *name, command_t cmds[])
102 {
103         command_t *cmd;
104
105         for (cmd = cmds; cmd->pc_name; cmd++) {
106                 if (strcmp(name, cmd->pc_name) == 0)
107                         return cmd;
108         }
109         return NULL;
110 }
111
112 int Parser_execarg(int argc, char **argv, command_t cmds[])
113 {
114         command_t *cmd;
115
116         cmd = Parser_findargcmd(argv[0], cmds);
117         if ( cmd ) {
118                 int rc = (cmd->pc_func)(argc, argv);
119                 if (rc == CMD_HELP)
120                         fprintf(stderr, "%s\n", cmd->pc_help);
121                 return rc;
122         } else {
123                 printf("Try interactive use without arguments or use one of:\n");
124                 for (cmd = cmds; cmd->pc_name; cmd++)
125                         printf("\"%s\" ", cmd->pc_name);
126                 printf("\nas argument.\n");
127         }
128         return -1;
129 }
130
131 /* returns the command_t * (NULL if not found) corresponding to a
132    _partial_ match with the first token in name.  It sets *next to
133    point to the following token. Does not modify *name. */
134 static command_t * find_cmd(char * name, command_t cmds[], char ** next)
135 {
136         int    i, len;
137     
138         if (!cmds || !name ) 
139                 return NULL;
140     
141         /* This sets name to point to the first non-white space character,
142            and next to the first whitespace after name, len to the length: do
143            this with strtok*/
144         name = skipwhitespace(name);
145         *next = skiptowhitespace(name);
146         len = *next - name;
147         if (len == 0) 
148                 return NULL;
149
150         for (i = 0; cmds[i].pc_name; i++) {
151                 if (strncasecmp(name, cmds[i].pc_name, len) == 0) {
152                         *next = skipwhitespace(*next);
153                         return(&cmds[i]);
154                 }
155         }
156         return NULL;
157 }
158
159 /* Recursively process a command line string s and find the command
160    corresponding to it. This can be ambiguous, full, incomplete,
161    non-existent. */
162 static int process(char *s, char ** next, command_t *lookup,
163                    command_t **result, char **prev)
164 {
165     *result = find_cmd(s, lookup, next);
166     *prev = s;
167
168         /* non existent */
169         if ( ! *result ) 
170                 return CMD_NONE;
171
172         /* found entry: is it ambigous, i.e. not exact command name and
173            more than one command in the list matches.  Note that find_cmd
174            points to the first ambiguous entry */
175         if ( strncasecmp(s, (*result)->pc_name, strlen((*result)->pc_name)) &&
176              find_cmd(s, (*result) + 1, next)) 
177                 return CMD_AMBIG;
178
179         /* found a unique command: component or full? */
180         if ( (*result)->pc_func ) {
181                 return CMD_COMPLETE;
182         } else {
183                 if ( *next == '\0' ) {
184                         return CMD_INCOMPLETE;
185                 } else {
186                         return process(*next, next, (*result)->pc_sub_cmd, result, prev);
187                 }
188         }
189 }
190
191 #ifdef HAVE_LIBREADLINE
192 static command_t * match_tbl;   /* Command completion against this table */
193 static char * command_generator(const char * text, int state)
194 {
195         static int index,
196                 len;
197         char       *name;
198
199         /* Do we have a match table? */
200         if (!match_tbl)
201                 return NULL;
202
203         /* If this is the first time called on this word, state is 0 */
204         if (!state) {
205                 index = 0;
206                 len = (int)strlen(text);
207         }
208
209         /* Return next name in the command list that paritally matches test */
210         while ( (name = (match_tbl + index)->pc_name) ) {
211                 index++;
212
213                 if (strncasecmp(name, text, len) == 0) {
214                         return(strdup(name));
215                 }
216         }
217
218     /* No more matches */
219     return NULL;
220 }
221
222 /* probably called by readline */
223 static char **command_completion(char * text, int start, int end)
224 {
225     command_t   * table;
226     char        * pos;
227
228     match_tbl = top_level;
229     
230     for (table = find_cmd(rl_line_buffer, match_tbl, &pos);
231          table; table = find_cmd(pos, match_tbl, &pos)) 
232     {
233
234         if (*(pos - 1) == ' ') match_tbl = table->pc_sub_cmd;
235     }
236
237     return completion_matches(text, command_generator);
238 }
239 #endif
240
241 /* take a string and execute the function or print help */
242 int execute_line(char * line)
243 {
244         command_t         *cmd, *ambig;
245         char *prev;
246         char *next, *tmp;
247         char *argv[MAXARGS];
248         int         i;
249         int rc = 0;
250
251         switch( process(line, &next, top_level, &cmd, &prev) ) {
252         case CMD_AMBIG:
253                 fprintf(stderr, "Ambiguous command \'%s\'\nOptions: ", line);
254                 while( (ambig = find_cmd(prev, cmd, &tmp)) ) {
255                         fprintf(stderr, "%s ", ambig->pc_name);
256                         cmd = ambig + 1;
257                 }
258                 fprintf(stderr, "\n");
259                 break;
260         case CMD_NONE:
261                 fprintf(stderr, "No such command, type help\n");
262                 break;
263         case CMD_INCOMPLETE:
264                 fprintf(stderr,
265                         "'%s' incomplete command.  Use '%s x' where x is one of:\n",
266                         line, line);
267                 fprintf(stderr, "\t");
268                 for (i = 0; cmd->pc_sub_cmd[i].pc_name; i++) {
269                         fprintf(stderr, "%s ", cmd->pc_sub_cmd[i].pc_name);
270                 }
271                 fprintf(stderr, "\n");
272                 break;
273         case CMD_COMPLETE:
274                 i = line2args(line, argv, MAXARGS);
275                 rc = (cmd->pc_func)(i, argv);
276
277                 if (rc == CMD_HELP)
278                         fprintf(stderr, "%s\n", cmd->pc_help);
279
280                 break;
281         }
282
283         return rc;
284 }
285
286 int
287 noop_fn ()
288 {
289         return (0);
290 }
291
292 /* just in case you're ever in an airplane and discover you 
293    forgot to install readline-dev. :) */
294 int init_input() 
295 {
296         int   interactive = isatty (fileno (stdin));
297
298 #ifdef HAVE_LIBREADLINE
299         using_history();
300         stifle_history(HISTORY);
301
302         if (!interactive)
303         {
304                 rl_prep_term_function = (rl_vintfunc_t *)noop_fn;
305                 rl_deprep_term_function = (rl_voidfunc_t *)noop_fn;
306         }
307
308         rl_attempted_completion_function = (CPPFunction *)command_completion;
309         rl_completion_entry_function = (void *)command_generator;
310 #endif 
311         return interactive;
312 }
313
314 #ifndef HAVE_LIBREADLINE
315 #define add_history(s)
316 char * readline(char * prompt) 
317 {
318         char line[2048];
319         int n = 0;
320         if (prompt)
321                 printf ("%s", prompt);
322         if (fgets(line, sizeof(line), stdin) == NULL)
323                 return (NULL);
324         n = strlen(line);
325         if (n && line[n-1] == '\n')
326                 line[n-1] = '\0';
327         return strdup(line);
328 }
329 #endif
330
331 /* this is the command execution machine */
332 int Parser_commands(void)
333 {
334         char *line, *s;
335         int rc = 0;
336         int interactive;
337         
338         interactive = init_input();
339
340         while(!done) {
341                 line = readline(interactive ? parser_prompt : NULL);
342
343                 if (!line) break;
344
345                 s = skipwhitespace(line);
346
347                 if (*s) {
348                         add_history(s);
349                         rc = execute_line(s);
350
351                         /* reset optind to 0 to tell getopt
352                          * to reinitialize itself */
353                         optind = 0;
354                 }
355                 
356                 free(line);
357         }
358         return rc;
359 }
360
361
362 /* sets the parser prompt */
363 void Parser_init(char * prompt, command_t * cmds)
364 {
365     done = 0;
366     top_level = cmds;
367     if (parser_prompt) free(parser_prompt);
368     parser_prompt = strdup(prompt);
369 }
370
371 /* frees the parser prompt */
372 void Parser_exit(int argc, char *argv[])
373 {
374     done = 1;
375     free(parser_prompt);
376     parser_prompt = NULL;
377 }
378
379 /* convert a string to an integer */
380 int Parser_int(char *s, int *val)
381 {
382     int ret;
383
384     if (*s != '0')
385         ret = sscanf(s, "%d", val);
386     else if (*(s+1) != 'x')
387         ret = sscanf(s, "%o", val);
388     else {
389         s++;
390         ret = sscanf(++s, "%x", val);
391     }
392
393     return(ret);
394 }
395
396
397 void Parser_qhelp(int argc, char *argv[]) {
398
399     printf("Available commands are:\n");
400
401     print_commands(NULL, top_level);
402     printf("For more help type: help command-name\n");
403 }
404
405 int Parser_help(int argc, char **argv) 
406 {
407         char line[1024];
408         char *next, *prev, *tmp;
409         command_t *result, *ambig;
410         int i;
411
412         if ( argc == 1 ) {
413                 Parser_qhelp(argc, argv);
414                 return 0;
415         }
416
417         line[0]='\0';
418         for ( i = 1 ;  i < argc ; i++ ) {
419                 strcat(line, argv[i]);
420         }
421
422         switch ( process(line, &next, top_level, &result, &prev) ) {
423         case CMD_COMPLETE:
424                 fprintf(stderr, "%s: %s\n",line, result->pc_help);
425                 break;
426         case CMD_NONE:
427                 fprintf(stderr, "%s: Unknown command.\n", line);
428                 break;
429         case CMD_INCOMPLETE:
430                 fprintf(stderr,
431                         "'%s' incomplete command.  Use '%s x' where x is one of:\n",
432                         line, line);
433                 fprintf(stderr, "\t");
434                 for (i = 0; result->pc_sub_cmd[i].pc_name; i++) {
435                         fprintf(stderr, "%s ", result->pc_sub_cmd[i].pc_name);
436                 }
437                 fprintf(stderr, "\n");
438                 break;
439         case CMD_AMBIG:
440                 fprintf(stderr, "Ambiguous command \'%s\'\nOptions: ", line);
441                 while( (ambig = find_cmd(prev, result, &tmp)) ) {
442                         fprintf(stderr, "%s ", ambig->pc_name);
443                         result = ambig + 1;
444                 }
445                 fprintf(stderr, "\n");
446                 break;
447         }
448         return 0;
449 }  
450
451
452 void Parser_printhelp(char *cmd)
453 {
454         char *argv[] = { "help", cmd }; 
455         Parser_help(2, argv);
456 }
457
458 /*************************************************************************
459  * COMMANDS                                                              *
460  *************************************************************************/
461
462
463 static void print_commands(char * str, command_t * table) {
464     command_t * cmds;
465     char        buf[80];
466
467     for (cmds = table; cmds->pc_name; cmds++) {
468         if (cmds->pc_func) {
469             if (str) printf("\t%s %s\n", str, cmds->pc_name);
470             else printf("\t%s\n", cmds->pc_name);
471         }
472         if (cmds->pc_sub_cmd) {
473             if (str) {
474                 sprintf(buf, "%s %s", str, cmds->pc_name);
475                 print_commands(buf, cmds->pc_sub_cmd);
476             } else {
477                 print_commands(cmds->pc_name, cmds->pc_sub_cmd);
478             }
479         }
480     }
481 }
482
483 char *Parser_getstr(const char *prompt, const char *deft, char *res,
484                     size_t len)
485 {
486     char *line = NULL;
487     int size = strlen(prompt) + strlen(deft) + 8;
488     char *theprompt;
489     theprompt = malloc(size);
490     assert(theprompt);
491
492     sprintf(theprompt, "%s [%s]: ", prompt, deft);
493
494     line  = readline(theprompt);
495     free(theprompt);
496
497     if ( line == NULL || *line == '\0' ) {
498         strncpy(res, deft, len);
499     } else {
500         strncpy(res, line, len);
501     }
502
503     if ( line ) {
504         free(line);
505         return res;
506     } else {
507         return NULL;
508     }
509 }
510
511 /* get integer from prompt, loop forever to get it */
512 int Parser_getint(const char *prompt, long min, long max, long deft, int base)
513 {
514     int rc;
515     long result;
516     char *line;
517     int size = strlen(prompt) + 40;
518     char *theprompt = malloc(size);
519     assert(theprompt);
520     sprintf(theprompt,"%s [%ld, (0x%lx)]: ", prompt, deft, deft);
521
522     fflush(stdout);
523
524     do {
525         line = NULL;
526         line = readline(theprompt);
527         if ( !line ) {
528             fprintf(stdout, "Please enter an integer.\n");
529             fflush(stdout);
530             continue;
531         }
532         if ( *line == '\0' ) {
533             free(line);
534             result =  deft;
535             break;
536         }
537         rc = Parser_arg2int(line, &result, base);
538         free(line);
539         if ( rc != 0 ) {
540             fprintf(stdout, "Invalid string.\n");
541             fflush(stdout);
542         } else if ( result > max || result < min ) {
543             fprintf(stdout, "Error: response must lie between %ld and %ld.\n",
544                     min, max);
545             fflush(stdout);
546         } else {
547             break;
548         }
549     } while ( 1 ) ;
550
551     if (theprompt)
552         free(theprompt);
553     return result;
554
555 }
556
557 /* get boolean (starting with YyNn; loop forever */
558 int Parser_getbool(const char *prompt, int deft)
559 {
560     int result = 0;
561     char *line;
562     int size = strlen(prompt) + 8;
563     char *theprompt = malloc(size);
564     assert(theprompt);
565
566     fflush(stdout);
567
568     if ( deft != 0 && deft != 1 ) {
569         fprintf(stderr, "Error: Parser_getbool given bad default (%d).\n",
570                 deft);
571         assert ( 0 );
572     }
573     sprintf(theprompt, "%s [%s]: ", prompt, (deft==0)? "N" : "Y");
574
575     do {
576         line = NULL;
577         line = readline(theprompt);
578         if ( line == NULL ) {
579             result = deft;
580             break;
581         }
582         if ( *line == '\0' ) {
583             result = deft;
584             break;
585         }
586         if ( *line == 'y' || *line == 'Y' ) {
587             result = 1;
588             break;
589         }
590         if ( *line == 'n' || *line == 'N' ) {
591             result = 0;
592             break;
593         }
594         if ( line )
595             free(line);
596         fprintf(stdout, "Invalid string. Must start with yY or nN\n");
597         fflush(stdout);
598     } while ( 1 );
599
600     if ( line )
601         free(line);
602     if ( theprompt )
603         free(theprompt);
604     return result;
605 }
606
607 /* parse int out of a string or prompt for it */
608 long Parser_intarg(const char *inp, const char *prompt, int deft,
609                   int min, int max, int base)
610 {
611     long result;
612     int rc;
613
614     rc = Parser_arg2int(inp, &result, base);
615
616     if ( rc == 0 ) {
617         return result;
618     } else {
619         return Parser_getint(prompt, deft, min, max, base);
620     }
621 }
622
623 /* parse int out of a string or prompt for it */
624 char *Parser_strarg(char *inp, const char *prompt, const char *deft,
625                     char *answer, int len)
626 {
627     if ( inp == NULL || *inp == '\0' ) {
628         return Parser_getstr(prompt, deft, answer, len);
629     } else
630         return inp;
631 }
632
633 /* change a string into a number: return 0 on success. No invalid characters
634    allowed. The processing of base and validity follows strtol(3)*/
635 int Parser_arg2int(const char *inp, long *result, int base)
636 {
637     char *endptr;
638
639     if ( (base !=0) && (base < 2 || base > 36) )
640         return 1;
641
642     *result = strtol(inp, &endptr, base);
643
644         if ( *inp != '\0' && *endptr == '\0' )
645                 return 0;
646         else 
647                 return 1;
648 }
649
650 int Parser_quit(int argc, char **argv)
651 {
652         argc = argc;
653         argv = argv;
654         done = 1;
655         return 0;
656 }