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