pull-prototypes-to-head-20020821
[openafs.git] / src / cmd / cmd.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID("$Header$");
14
15 #include <sys/types.h>
16 #include <ctype.h>
17 #include "cmd.h"
18 #include <stdio.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <assert.h>
22
23 /* declaration of private token type */
24 struct cmd_token {
25     struct cmd_token *next;
26     char *key;
27 };
28
29 static int dummy;       /* non-null ptr used for flag existence */
30 static struct cmd_syndesc *allSyntax=0;
31 static int noOpcodes = 0;
32 static int (*beforeProc)(struct cmd_syndesc *ts, char *beforeRock) = 0, (*afterProc)(struct cmd_syndesc *ts, char *afterRock) = 0;
33 static char *beforeRock, *afterRock;
34 static char initcmd_opcode[] = "initcmd";    /*Name of initcmd opcode*/
35
36 /* take name and string, and return null string if name is empty, otherwise return
37    the concatenation of the two strings */
38 static char *NName(char *a1, char *a2)
39 {
40     static char tbuffer[80];
41     if (strlen(a1) == 0) {
42         return "";
43     }
44     else {
45         strcpy(tbuffer, a1);
46         strcat(tbuffer, a2);
47         return tbuffer;
48     }
49 }
50
51 /* return true if asub is a substring of amain */
52 static int SubString(register char *amain, register char *asub)
53 {
54     int mlen, slen;
55     register int i,j;
56     mlen = strlen(amain);
57     slen = strlen(asub);
58     j = mlen - slen;
59     if (j < 0) return 0;        /* not a substring */
60     for(i=0;i<=j;i++) {
61         if (strncmp(amain, asub, slen) == 0) return 1;
62         amain++;
63     }
64     return 0;   /* didn't find it */
65 }
66
67 static int FindType(register struct cmd_syndesc *as, register char *aname)
68 {
69     register int i;
70     size_t cmdlen;
71     int ambig;
72     int best;
73
74     cmdlen = strlen(aname);
75     ambig = 0;
76     best = -1;
77     for(i=0;i<CMD_MAXPARMS;i++) {
78         if (as->parms[i].type == 0) continue;   /* this slot not set (seeked over) */
79         if (strcmp(as->parms[i].name, aname)==0) return i;
80         if (strlen(as->parms[i].name) < cmdlen) continue;
81         /* A hidden option must be a full match (no best matches) */
82         if (as->parms[i].flags & CMD_HIDE) continue;
83
84         if (strncmp(as->parms[i].name, aname, cmdlen) == 0) {
85             if (best != -1) ambig = 1;
86             else best = i;
87         }
88     }
89     return (ambig? -1 : best);
90 }
91
92 static struct cmd_syndesc *FindSyntax(char *aname, int *aambig)
93 {
94     register struct cmd_syndesc *ts;
95     struct cmd_syndesc *best;
96     size_t cmdLen;
97     int ambig;
98
99     cmdLen = strlen(aname);
100     best = (struct cmd_syndesc *) 0;
101     ambig = 0;
102     if (aambig) *aambig = 0;    /* initialize to unambiguous */
103     for(ts=allSyntax;ts;ts=ts->next) {
104         if (strcmp(aname, ts->name)==0) return(ts);
105         if (strlen(ts->name) < cmdLen) continue;    /* we typed more than item has */
106         /* A hidden command must be a full match (no best matches) */
107         if (ts->flags & CMD_HIDDEN) continue;
108
109         /* This is just an alias for *best, or *best is just an alias for us.
110          * If we don't make this check explicitly, then an alias which is just a
111          * short prefix of the real command's name might make things ambiguous
112          * for no apparent reason.
113          */
114         if (best && ts->aliasOf == best->aliasOf) continue;
115         if (strncmp(ts->name, aname, cmdLen) == 0) {
116             if (best) ambig = 1;        /* ambiguous name */
117             else
118                 best = ts;
119         }
120     }
121     if (ambig) {
122         if(aambig) *aambig = ambig;     /* if ambiguous and they care, tell them */
123         return (struct cmd_syndesc *) 0;        /* fails */
124     }
125     else return best;   /* otherwise its not ambiguous, and they know */
126 }
127
128 /* print the help for a single parameter */
129 static void PrintParmHelp(register struct cmd_parmdesc *aparm)
130 {
131     if (aparm->type == CMD_FLAG) {
132 #ifdef notdef
133         /* doc people don't like seeing this information */
134         if (aparm->help)
135             printf(" (%s)", aparm->help);
136 #endif
137     }
138     else if (aparm->help) {
139         printf(" <%s>", aparm->help);
140         if (aparm->type == CMD_LIST) printf("+");
141     }
142     else if (aparm->type == CMD_SINGLE)
143         printf(" <arg>");
144     else if (aparm->type == CMD_LIST)
145         printf(" <arg>+");
146 }
147
148 void PrintSyntax(register struct cmd_syndesc *as)
149 {
150     register int i;
151     register struct cmd_parmdesc *tp;
152
153     /* now print usage, from syntax table */
154     if (noOpcodes)
155         printf("Usage: %s", as->a0name);
156     else {
157         if (!strcmp(as->name, initcmd_opcode))
158             printf("Usage: %s[%s]", NName(as->a0name, " "), as->name);
159         else
160             printf("Usage: %s%s", NName(as->a0name, " "), as->name);
161     }
162
163     for(i=0;i<CMD_MAXPARMS;i++) {
164         tp = &as->parms[i];
165         if (tp->type == 0) continue;    /* seeked over slot */
166         if (tp->flags & CMD_HIDE) continue;  /* skip hidden options */
167         printf(" ");
168         if (tp->flags & CMD_OPTIONAL) printf("[");
169         printf("%s", tp->name);
170         PrintParmHelp(tp);
171         if (tp->flags & CMD_OPTIONAL) printf("]");
172     }
173     printf("\n");
174 }
175
176 /* must print newline in any case, to terminate preceding line */
177 static void PrintAliases(register struct cmd_syndesc *as)
178 {
179     register struct cmd_syndesc *ts;
180
181     if (as->flags & CMD_ALIAS) {
182         ts = as->aliasOf;
183         printf("(alias for %s)\n", ts->name);
184     }
185     else {
186         printf("\n");
187         if (!as->nextAlias) return;         /* none, print nothing */
188         printf("aliases: ");
189         for(as=as->nextAlias;as;as=as->nextAlias) {
190             printf("%s ", as->name);
191         }
192         printf("\n");
193     }
194 }
195
196 void PrintFlagHelp(register struct cmd_syndesc *as)
197 {
198     register int i;
199     register struct cmd_parmdesc *tp;
200     size_t flag_width;
201     char *flag_prefix;
202
203     /* find flag name length */
204     flag_width = 0;
205     for(i=0;i<CMD_MAXPARMS;i++) {
206         if (i == CMD_HELPPARM) continue;
207         tp = &as->parms[i];
208         if (tp->type != CMD_FLAG) continue;
209         if (tp->flags & CMD_HIDE) continue;  /* skip hidden options */
210         if (!tp->help) continue;
211
212         if (strlen(tp->name) > flag_width) flag_width = strlen(tp->name);
213     }
214
215     /* print flag help */
216     flag_prefix = "Where:";
217     for(i=0;i<CMD_MAXPARMS;i++) {
218         if (i == CMD_HELPPARM) continue;
219         tp = &as->parms[i];
220         if (tp->type != CMD_FLAG) continue;
221         if (tp->flags & CMD_HIDE) continue;  /* skip hidden options */
222         if (!tp->help) continue;
223
224         printf("%-7s%-*s  %s\n", flag_prefix, flag_width, tp->name, tp->help);
225         flag_prefix = "";
226     }
227 }
228
229 static int AproposProc(register struct cmd_syndesc *as, char *arock)
230 {
231     register struct cmd_syndesc *ts;
232     char *tsub;
233     int didAny;
234
235     didAny = 0;
236     tsub = as->parms[0].items->data;
237     for(ts=allSyntax;ts;ts=ts->next) {
238         if ((ts->flags & CMD_ALIAS) || (ts->flags & CMD_HIDDEN))
239            continue;
240         if (SubString(ts->help, tsub)) {
241             printf("%s: %s\n", ts->name, ts->help);
242             didAny = 1;
243         }
244         else if (SubString(ts->name, tsub)) {
245             printf("%s: %s\n", ts->name, ts->help);
246             didAny = 1;
247         }
248     }
249     if (!didAny)
250         printf("Sorry, no commands found\n");
251     return 0;
252 }
253
254 static int HelpProc(register struct cmd_syndesc *as, char *arock)
255 {
256     register struct cmd_syndesc *ts;
257     register struct cmd_item *ti;
258     int ambig;
259     int code = 0;
260
261     if (as->parms[0].items == 0) {
262         printf("%sCommands are:\n", NName(as->a0name, ": "));
263         for(ts=allSyntax; ts; ts=ts->next) {
264             if ((ts->flags & CMD_ALIAS) || (ts->flags & CMD_HIDDEN))
265                continue;
266             printf("%-15s %s\n", ts->name, (ts->help?ts->help:""));
267         }
268     }
269     else {
270         /* print out individual help topics */
271         for(ti = as->parms[0].items; ti; ti=ti->next) {
272             code = 0;
273             ts = FindSyntax(ti->data, &ambig);
274             if (ts && (ts->flags & CMD_HIDDEN)) ts = 0;          /* no hidden commands */
275             if (ts) {
276                 /* print out command name and help */
277                 printf("%s%s: %s ", NName(as->a0name, " "), ts->name, (ts->help?ts->help:""));
278                 ts->a0name = as->a0name;
279                 PrintAliases(ts);
280                 PrintSyntax(ts);
281                 PrintFlagHelp(ts);
282             }
283             else {
284                 if (!ambig)
285                     fprintf(stderr, "%sUnknown topic '%s'\n", NName(as->a0name, ": "), ti->data);
286                 else {
287                     /* ambiguous, list 'em all */
288                     fprintf(stderr, "%sAmbiguous topic '%s'; use 'apropos' to list\n",
289                            NName(as->a0name, ": "), ti->data);
290                 }
291                 code = CMD_UNKNOWNCMD;
292             }
293         }
294     }
295     return(code);
296 }
297
298 int cmd_SetBeforeProc(int (*aproc)(struct cmd_syndesc *ts, char *beforeRock), char *arock)
299 {
300     beforeProc = aproc;
301     beforeRock = arock;
302     return 0;
303 }
304
305 int cmd_SetAfterProc(int (*aproc)(struct cmd_syndesc *ts, char *afterRock), char *arock)
306 {
307     afterProc = aproc;
308     afterRock = arock;
309     return 0;
310 }
311
312 /* thread on list in alphabetical order */
313 static int SortSyntax(struct cmd_syndesc *as)
314 {
315     register struct cmd_syndesc **ld, *ud;
316
317     for(ld = &allSyntax, ud = *ld; ud; ld = &ud->next, ud = *ld) {
318         if (strcmp(ud->name, as->name) > 0) { /* next guy is bigger than us */
319             break;
320         }
321     }
322     /* thread us on the list now */
323     *ld = as;
324     as->next = ud;
325     return 0;
326 }
327
328 struct cmd_syndesc *cmd_CreateSyntax(char *aname, int (*aproc)(struct cmd_syndesc *ts, char *arock), char *arock, char *ahelp)
329 {
330     register struct cmd_syndesc *td;
331     
332     /* can't have two cmds in no opcode mode */
333     if (noOpcodes) return (struct cmd_syndesc *) 0;
334
335     td = (struct cmd_syndesc *) calloc(1, sizeof(struct cmd_syndesc));
336     assert(td);
337     td->aliasOf = td;   /* treat aliasOf as pointer to real command, no matter what */
338     
339     /* copy in name, etc */
340     if (aname) {
341         td->name = (char *) malloc(strlen(aname)+1);
342         assert(td->name);
343         strcpy(td->name, aname);
344     }
345     else {
346         td->name = NULL;
347         noOpcodes = 1;
348     }
349     if (ahelp) {
350         /* Piggy-back the hidden option onto the help string */
351         if (ahelp == (char *)CMD_HIDDEN) {
352             td->flags |= CMD_HIDDEN;
353         } else {
354             td->help = (char *) malloc(strlen(ahelp)+1);
355             assert(td->help);
356             strcpy(td->help, ahelp);
357         }
358     }
359     else td->help = NULL;
360     td->proc = aproc;
361     td->rock = arock;
362     
363     SortSyntax(td);
364
365     cmd_Seek(td, CMD_HELPPARM);
366     cmd_AddParm(td, "-help", CMD_FLAG, CMD_OPTIONAL, "get detailed help");
367     cmd_Seek(td, 0);
368
369     return td;
370 }
371
372 int cmd_CreateAlias(register struct cmd_syndesc *as, char *aname)
373 {
374     register struct cmd_syndesc *td;
375     
376     td = (struct cmd_syndesc *) malloc(sizeof(struct cmd_syndesc));
377     assert(td);
378     memcpy(td, as, sizeof(struct cmd_syndesc));
379     td->name = (char *) malloc(strlen(aname)+1);
380     assert(td->name);
381     strcpy(td->name, aname);
382     td->flags |= CMD_ALIAS;
383     /* if ever free things, make copy of help string, too */
384     
385     /* thread on list */
386     SortSyntax(td);
387     
388     /* thread on alias lists */
389     td->nextAlias = as->nextAlias;
390     as->nextAlias = td;
391     td->aliasOf = as;
392
393     return 0;   /* all done */
394 }
395
396 int cmd_IsAdministratorCommand(register struct cmd_syndesc *as)
397 {
398     as->flags |= CMD_ADMIN;
399     return 0;
400
401
402 int cmd_Seek(register struct cmd_syndesc *as, int apos)
403 {
404     if (apos >= CMD_MAXPARMS) return CMD_EXCESSPARMS;
405     as->nParms = apos;
406     return 0;
407 }
408
409 int cmd_AddParm(register struct cmd_syndesc *as, char *aname, int atype, 
410         afs_int32 aflags, char *ahelp)
411 {
412     register struct cmd_parmdesc *tp;
413
414     if (as->nParms >= CMD_MAXPARMS) return CMD_EXCESSPARMS;
415     tp = &as->parms[as->nParms++];
416     
417     tp->name = (char *) malloc(strlen(aname)+1);
418     assert(tp->name);
419     strcpy(tp->name, aname);
420     tp->type = atype;
421     tp->flags = aflags;
422     tp->items = (struct cmd_item *) 0;
423     if (ahelp) {
424         tp->help = (char *) malloc(strlen(ahelp)+1);
425         assert(tp->help);
426         strcpy(tp->help, ahelp);
427     }
428     else tp->help = NULL;
429     return 0;
430 }
431
432 /* add a text item to the end of the parameter list */
433 static int AddItem(register struct cmd_parmdesc *aparm, register char *aval)
434 {
435     register struct cmd_item *ti, *ni;
436     ti = (struct cmd_item *) calloc(1, sizeof(struct cmd_item));
437     assert(ti);
438     ti->data = (char *) malloc(strlen(aval)+1);
439     assert(ti->data);
440     strcpy(ti->data, aval);
441     /* now put ti at the *end* of the list */
442     if ((ni=aparm->items)) {
443         for(;ni;ni=ni->next) if (ni->next == 0) break;  /* skip to last one */
444         ni->next = ti;
445     }
446     else aparm->items = ti; /* we're first */
447     return 0;
448 }
449
450 /* skip to next non-flag item, if any */
451 static int AdvanceType(register struct cmd_syndesc *as, register afs_int32 aval)
452 {
453     register afs_int32 next;
454     register struct cmd_parmdesc *tp;
455
456     /* first see if we should try to grab rest of line for this dude */
457     if (as->parms[aval].flags & CMD_EXPANDS) return aval;
458
459     /* if not, find next non-flag used slot */
460     for(next=aval+1;next<CMD_MAXPARMS;next++) {
461         tp = &as->parms[next];
462         if (tp->type != 0 && tp->type != CMD_FLAG) return next;
463     }
464     return aval;
465 }
466
467 /* discard parameters filled in by dispatch */
468 static void ResetSyntax(register struct cmd_syndesc *as)
469 {
470     int i;
471     register struct cmd_parmdesc *tp;
472     register struct cmd_item *ti, *ni;
473
474     tp = as->parms;
475     for(i=0;i<CMD_MAXPARMS;i++, tp++) {
476         switch(tp->type) {
477             case CMD_SINGLE:
478             case CMD_LIST:
479                 /* free whole list in both cases, just for fun */
480                 for(ti=tp->items;ti;ti=ni) {
481                     ni = ti->next;
482                     free(ti->data);
483                     free(ti);
484                 }
485                 break;
486
487             default:
488                 break;
489         }
490         tp->items = (struct cmd_item *)0;
491     }
492 }
493
494 /* move the expands flag to the last one in the list */
495 static int SetupExpandsFlag(register struct cmd_syndesc *as)
496 {
497     register struct cmd_parmdesc *tp;
498     register int last, i;
499
500     last = -1;
501     /* find last CMD_LIST type parameter, optional or not, and make it expandable
502         if no other dude is expandable */
503     for(i=0;i<CMD_MAXPARMS;i++) {
504         tp = &as->parms[i];
505         if (tp->type == CMD_LIST) {
506             if (tp->flags & CMD_EXPANDS) return 0;      /* done if already specified */
507             last = i;
508         }
509     }
510     if (last >= 0) as->parms[last].flags |= CMD_EXPANDS;
511     return 0;
512 }
513
514 /*Take the current argv & argc and alter them so that the initialization opcode is made to appear.  This is used in cases where the initialization opcode is implicitly invoked.*/
515 static char **InsertInitOpcode(int *aargc, char **aargv)
516 {
517     char **newargv;     /*Ptr to new, expanded argv space*/
518     char *pinitopcode;  /*Ptr to space for name of init opcode*/
519     int i;              /*Loop counter*/
520
521     /*Allocate the new argv array, plus one for the new opcode, plus one more for the trailing null pointer*/
522     newargv = (char **) malloc(((*aargc)+2) * sizeof(char *));
523     if (!newargv) {
524       fprintf(stderr, "%s: Can't create new argv array with %d+2 slots\n", aargv[0], *aargc);
525         return(NULL);
526     }
527
528     /*Create space for the initial opcode & fill it in*/
529     pinitopcode = (char *) malloc(sizeof(initcmd_opcode));
530     if (!pinitopcode) {
531         fprintf(stderr, "%s: Can't malloc initial opcode space\n", aargv[0]);
532         return(NULL);
533     }
534     strcpy(pinitopcode, initcmd_opcode);
535
536     /*Move all the items in the old argv into the new argv, in their proper places*/
537     for (i = *aargc; i>1; i--)
538         newargv[i] = aargv[i-1];
539
540     /*Slip in the opcode and the trailing null pointer, and bump the argument count up by one for the new opcode*/
541     newargv[0] = aargv[0];
542     newargv[1] = pinitopcode;
543     (*aargc)++;
544     newargv[*aargc] = NULL;
545
546     /*Return the happy news*/
547     return(newargv);
548
549 } /*InsertInitOpcode*/
550
551 static int NoParmsOK(register struct cmd_syndesc *as)
552 {
553     register int i;
554     register struct cmd_parmdesc *td;
555
556     for(i=0;i<CMD_MAXPARMS;i++) {
557         td = &as->parms[i];
558         if (td->type != 0 && !(td->flags & CMD_OPTIONAL)) {
559             /* found a non-optional (e.g. required) parm, so NoParmsOK
560                is false (some parms are required) */
561             return 0;
562         }
563     }
564     return 1;
565 }
566
567 /* Call the appropriate function, or return syntax error code.  Note: if no opcode is specified, an initialization routine exists, and it has NOT been called before, we invoke the special initialization opcode*/
568 int cmd_Dispatch(int argc, char **argv)
569 {
570     char *pname;
571     struct cmd_syndesc *ts;
572     struct cmd_parmdesc *tparm;
573     afs_int32 i, j;
574     int curType;
575     int positional;
576     int ambig;
577     static int initd = 0;           /*Is this the first time this routine has been called?*/
578     static int initcmdpossible = 1; /*Should be consider parsing the initial command?*/
579
580     if (!initd) {
581         initd = 1;
582         /* Add help, apropos commands once */
583         if (!noOpcodes) {
584             ts = cmd_CreateSyntax("help", HelpProc, (char*)0,
585                                   "get help on commands");
586             cmd_AddParm(ts, "-topic", CMD_LIST, CMD_OPTIONAL, "help string");
587             cmd_AddParm(ts, "-admin", CMD_FLAG, CMD_OPTIONAL, NULL);
588
589             ts = cmd_CreateSyntax("apropos", AproposProc, (char*)0,
590                                   "search by help text");
591             cmd_AddParm(ts, "-topic", CMD_SINGLE, CMD_REQUIRED, "help string");
592         }
593     }
594
595     /*Remember the program name*/
596     pname = argv[0];
597
598     if (noOpcodes) {
599         if (argc == 1) {
600             if (!NoParmsOK(allSyntax)) {
601                 printf("%s: Type '%s -help' for help\n", pname, pname);
602                 return(CMD_USAGE);
603             }
604         }
605     }
606     else {
607         if (argc < 2) {
608             /* if there is an initcmd, don't print an error message, just
609                setup to use the initcmd below. */
610             if (!(initcmdpossible && FindSyntax(initcmd_opcode, (int *) 0))) {
611                 printf("%s: Type '%s help' or '%s help <topic>' for help\n",
612                        pname, pname, pname);
613                 return(CMD_USAGE);
614             }
615         }
616     }
617     
618     /* Find the syntax descriptor for this command, doing prefix matching properly */
619     if (noOpcodes) {
620         ts = allSyntax;
621     }
622     else {
623         ts = (argc < 2? 0 : FindSyntax(argv[1], &ambig));
624         if (!ts) {
625             /*First token doesn't match a syntax descriptor*/
626             if (initcmdpossible) {
627                 /*If initial command line handling hasn't been done yet,
628                   see if there is a descriptor for the initialization opcode.
629                   Only try this once.*/
630                 initcmdpossible = 0;
631                 ts = FindSyntax(initcmd_opcode, (int *) 0);
632                 if (!ts) {
633                     /*There is no initialization opcode available, so we declare
634                       an error*/
635                     if (ambig) {
636                         fprintf(stderr, "%s", NName(pname, ": "));
637                         fprintf(stderr, "Ambiguous operation '%s'; type '%shelp' for list\n",
638                                 argv[1], NName(pname," "));
639                     }
640                     else {
641                         fprintf(stderr, "%s", NName(pname, ": "));
642                         fprintf(stderr, "Unrecognized operation '%s'; type '%shelp' for list\n",
643                                 argv[1], NName(pname, " "));
644                     }
645                     return(CMD_UNKNOWNCMD);
646                 }
647                 else {
648                     /*Found syntax structure for an initialization opcode.  Fix
649                       up argv and argc to relect what the user
650                       ``should have'' typed*/
651                     if (!(argv = InsertInitOpcode(&argc, argv))) {
652                         fprintf(stderr, "%sCan't insert implicit init opcode into command line\n",
653                                 NName(pname, ": "));
654                         return(CMD_INTERNALERROR);
655                     }
656                 }
657             } /*Initial opcode not yet attempted*/
658             else {
659                 /* init cmd already run and no syntax entry found */
660                 if (ambig) {
661                     fprintf(stderr, "%s", NName(pname, ": "));
662                     fprintf(stderr, "Ambiguous operation '%s'; type '%shelp' for list\n",
663                             argv[1], NName(pname, " "));
664                 }
665                 else {
666                     fprintf(stderr, "%s", NName(pname, ": "));
667                     fprintf(stderr, "Unrecognized operation '%s'; type '%shelp' for list\n",
668                             argv[1], NName(pname, " "));
669                 }
670                 return CMD_UNKNOWNCMD;
671             }
672         } /*Argv[1] is not a valid opcode*/
673     } /*Opcodes are defined*/
674     
675     /* Found the descriptor; start parsing.  curType is the type we're trying to parse */
676     curType = 0;
677
678     /* We start off parsing in "positional" mode, where tokens are put in
679        slots positionally.  If we find a name that takes args, we go
680        out of positional mode, and from that point on, expect a switch
681        before any particular token. */
682     positional = 1; /* Are we still in the positional region of the cmd line? */
683     i = noOpcodes? 1 : 2;
684     SetupExpandsFlag(ts);
685     for(;i<argc; i++) {
686         /* Only tokens that start with a hyphen and are not followed by a digit
687          * are considered switches.  This allow negative numbers. */
688         if ((argv[i][0] == '-') && !isdigit(argv[i][1])) {
689             /* Find switch */
690             j = FindType(ts, argv[i]);
691             if (j < 0) {
692                 fprintf(stderr, "%sUnrecognized or ambiguous switch '%s'; type ",
693                         NName(pname, ": "), argv[i]);
694                 if (noOpcodes)
695                     fprintf(stderr, "'%s -help' for detailed help\n", argv[0]);
696                 else
697                     fprintf(stderr, "'%shelp %s' for detailed help\n",
698                             NName(argv[0], " "), ts->name);
699                 ResetSyntax(ts);
700                 return(CMD_UNKNOWNSWITCH);
701             }
702             if (j >= CMD_MAXPARMS) {
703                 fprintf(stderr, "%sInternal parsing error\n", NName(pname, ": "));
704                 ResetSyntax(ts);
705                 return(CMD_INTERNALERROR);
706             }
707             if (ts->parms[j].type == CMD_FLAG) {
708                 ts->parms[j].items = (struct cmd_item *) &dummy;
709             }
710             else {
711                 positional = 0;
712                 curType = j;
713                 ts->parms[j].flags |= CMD_PROCESSED;
714             }
715         }
716         else {
717             /* Try to fit in this descr */
718             if (curType >= CMD_MAXPARMS) {
719                 fprintf(stderr, "%sToo many arguments\n", NName(pname, ": "));
720                 ResetSyntax(ts);
721                 return(CMD_TOOMANY);
722             }
723             tparm = &ts->parms[curType];
724
725             if ((tparm->type == 0)         ||    /* No option in this slot */
726                 (tparm->type == CMD_FLAG)) {     /* A flag (not an argument */
727                 /* skipped parm slot */
728                 curType++;  /* Skip this slot and reprocess this parm */
729                 i--;
730                 continue;
731             }
732                 
733             if (!(tparm->flags & CMD_PROCESSED) && (tparm->flags & CMD_HIDE)) {
734                 curType++;  /* Skip this slot and reprocess this parm */
735                 i--;
736                 continue;
737             }
738
739             if (tparm->type == CMD_SINGLE) {
740                 if (tparm->items) {
741                     fprintf(stderr, "%sToo many values after switch %s\n",
742                             NName(pname, ": "), tparm->name);
743                     ResetSyntax(ts);
744                     return(CMD_NOTLIST);
745                 }
746                 AddItem(tparm, argv[i]);    /* Add to end of list */
747             }
748             else if (tparm->type == CMD_LIST) {
749                 AddItem(tparm, argv[i]);    /* Add to end of list */
750             }
751             /* Now, if we're in positional mode, advance to the next item */
752             if (positional)
753                 curType = AdvanceType(ts, curType);
754         }
755     }
756     
757     /* keep track of this for messages */
758     ts->a0name = argv[0];
759
760     /* If we make it here, all the parameters are filled in.  Check to see if this
761        is a -help version.  Must do this before checking for all required parms,
762        otherwise it is a real nuisance */
763     if (ts->parms[CMD_HELPPARM].items) {
764         PrintSyntax(ts);
765         ResetSyntax(ts);
766         return 0;
767     }
768
769     /* Parsing done, see if we have all of our required parameters */
770     for(i=0; i < CMD_MAXPARMS; i++) {
771         tparm = &ts->parms[i];
772         if (tparm->type == 0) continue; /* Skipped parm slot */
773         if ((tparm->flags & CMD_PROCESSED) && tparm->items == 0) {
774             fprintf(stderr, "%s The field '%s' isn't completed properly\n",
775                     NName(pname, ": "), tparm->name);
776             ResetSyntax(ts);
777             tparm->flags &= ~CMD_PROCESSED;
778             return(CMD_TOOFEW);
779         }
780         if (!(tparm->flags & CMD_OPTIONAL) && tparm->items == 0) {
781             fprintf(stderr, "%sMissing required parameter '%s'\n",
782                     NName(pname, ": "), tparm->name);
783             ResetSyntax(ts);
784             tparm->flags &= ~CMD_PROCESSED;
785             return(CMD_TOOFEW);
786         }
787         tparm->flags &= ~CMD_PROCESSED;
788     }
789     
790     /*
791      * Before calling the beforeProc and afterProc and all the implications 
792      * from those calls, check if the help procedure was called and call it now.
793      */
794     if ((ts->proc == HelpProc) || (ts->proc == AproposProc))
795     {
796         i = (*ts->proc) (ts, ts->rock);
797         ResetSyntax(ts);
798         return(i);
799     }
800
801     /* Now, we just call the procedure and return */
802     if (beforeProc)
803         i = (*beforeProc)(ts, beforeRock);
804     else
805         i = 0;
806     if (i) {
807         ResetSyntax(ts);
808         return(i);
809     }
810     i=(*ts->proc) (ts, ts->rock);
811     if (afterProc) (*afterProc)(ts, afterRock);
812     ResetSyntax(ts);    /* Reset and free things */
813     return(i);
814 }
815
816 /* free token list returned by parseLine */
817 static int FreeTokens(register struct cmd_token *alist)
818 {
819     register struct cmd_token *nlist;
820     for(; alist; alist = nlist) {
821         nlist = alist->next;
822         free(alist->key);
823         free(alist);
824     }
825     return 0;
826 }
827
828 /* free an argv list returned by parseline */
829 int cmd_FreeArgv(register char **argv)
830 {
831     register char *tp;
832     for(tp = *argv; tp; argv++, tp = *argv)
833         free(tp);
834     return 0;
835 }
836
837 /* copy back the arg list to the argv array, freeing the cmd_tokens as you go; the actual
838     data is still malloc'd, and will be freed when the caller calls cmd_FreeArgv
839     later on */
840 #define INITSTR ""
841 static int CopyBackArgs(register struct cmd_token *alist, register char **argv, 
842         afs_int32 *an, afs_int32 amaxn)
843 {
844     register struct cmd_token *next;
845     afs_int32 count;
846
847     count = 0;
848     if (amaxn <= 1) return CMD_TOOMANY;
849     *argv = (char *) malloc(strlen(INITSTR)+1);
850     assert(*argv);
851     strcpy(*argv, INITSTR);
852     amaxn--;
853     argv++;
854     count++;
855     while (alist) {
856         if (amaxn <= 1) return CMD_TOOMANY; /* argv is too small for his many parms. */
857         *argv = alist->key;
858         next = alist->next;
859         free(alist);
860         alist = next;
861         amaxn--;
862         argv++;
863         count++;
864     }
865     *(argv++) = 0;      /* use last slot for terminating null */
866     /* don't count terminating null */
867     *an = count;
868     return 0;
869 }
870
871 static int quote(register int x)
872 {
873     if (x == '"' || x == 39 /* single quote */) return 1;
874     else return 0;
875 }
876
877 static int space(register int x)
878 {
879     if (x == 0 || x == ' ' || x == '\t' || x== '\n') return 1;
880     else return 0;
881 }
882
883 int cmd_ParseLine(char *aline, char **argv, afs_int32 *an, afs_int32 amaxn)
884 {
885     char tbuffer[256];
886     register char *tptr = 0;
887     int inToken, inQuote;
888     struct cmd_token *first, *last;
889     register struct cmd_token *ttok;
890     register int tc;
891     
892     inToken = 0;        /* not copying token chars at start */
893     first = (struct cmd_token *) 0;
894     last = (struct cmd_token *) 0;
895     inQuote=0;          /* not in a quoted string */
896     while (1) {
897         tc = *aline++;
898         if (tc == 0 || (!inQuote && space(tc))) {    /* terminating null gets us in here, too */
899             if (inToken) {
900                 inToken = 0;    /* end of this token */
901                 if ( !tptr )
902                     return -1; /* should never get here */
903                 else
904                     *tptr++ = 0;
905                 ttok = (struct cmd_token *) malloc(sizeof(struct cmd_token));
906                 assert(ttok);
907                 ttok->next = (struct cmd_token *) 0;
908                 ttok->key = (char *) malloc(strlen(tbuffer)+1);
909                 assert(ttok->key);
910                 strcpy(ttok->key, tbuffer);
911                 if (last) {
912                     last->next = ttok;
913                     last = ttok;
914                 }
915                 else last = ttok;
916                 if (!first) first = ttok;
917             }
918         }
919         else {
920             /* an alpha character */
921             if (!inToken) {
922                 tptr = tbuffer;
923                 inToken = 1;
924             }
925             if (tptr - tbuffer >= sizeof(tbuffer)) {
926                 FreeTokens(first);
927                 return CMD_TOOBIG;   /* token too long */
928             }
929             if (quote(tc)) {
930                 /* hit a quote, toggle inQuote flag but don't insert character */
931                 inQuote = !inQuote;
932             }
933             else {
934                 /* insert character */
935                 *tptr++ = tc;
936             }
937         }
938         if (tc == 0) {
939             /* last token flushed 'cause space(0) --> true */
940             if (last) last->next = (struct cmd_token *) 0;
941             return CopyBackArgs(first, argv, an, amaxn);
942         }
943     }
944 }