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