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