From e1414be76bed00d12030e2704dc6e7bcca04b6d1 Mon Sep 17 00:00:00 2001 From: Simon Wilkinson Date: Tue, 19 Apr 2011 11:47:08 +0100 Subject: [PATCH] cmd: Split up dispatch function Split up the command line parsing behaviour out of the cmd_Dispatch function, and into a function of its own - cmd_Parse. This lets servers which only have a single "syntax" installed just parse, without needing to go through a dispatch function, and all of the control flow complexity that requires. Change-Id: Ic4325bf933ee5b28cb80db36ef9b2b5fb6763f41 Reviewed-on: http://gerrit.openafs.org/4541 Tested-by: BuildBot Reviewed-by: Derrick Brashear --- src/cmd/cmd.c | 159 ++++++++++++++++++++++++++---------------- src/cmd/cmd.p.h | 3 + tests/cmd/command-t.c | 45 +++++++++++- 3 files changed, 145 insertions(+), 62 deletions(-) diff --git a/src/cmd/cmd.c b/src/cmd/cmd.c index 087e05cdf..1f418179a 100644 --- a/src/cmd/cmd.c +++ b/src/cmd/cmd.c @@ -661,47 +661,57 @@ NoParmsOK(struct cmd_syndesc *as) return 1; } +/* Add help, apropos commands once */ +static void +initSyntax(void) +{ + struct cmd_syndesc *ts; + + if (!noOpcodes) { + ts = cmd_CreateSyntax("help", HelpProc, NULL, + "get help on commands"); + cmd_AddParm(ts, "-topic", CMD_LIST, CMD_OPTIONAL, "help string"); + cmd_AddParm(ts, "-admin", CMD_FLAG, CMD_OPTIONAL, NULL); + + ts = cmd_CreateSyntax("apropos", AproposProc, NULL, + "search by help text"); + cmd_AddParm(ts, "-topic", CMD_SINGLE, CMD_REQUIRED, "help string"); + ts = cmd_CreateSyntax("version", VersionProc, NULL, + (char *)CMD_HIDDEN); + ts = cmd_CreateSyntax("-version", VersionProc, NULL, + (char *)CMD_HIDDEN); + ts = cmd_CreateSyntax("-help", HelpProc, NULL, + (char *)CMD_HIDDEN); + ts = cmd_CreateSyntax("--version", VersionProc, NULL, + (char *)CMD_HIDDEN); + ts = cmd_CreateSyntax("--help", HelpProc, NULL, + (char *)CMD_HIDDEN); + } +} + /* 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 */ int -cmd_Dispatch(int argc, char **argv) +cmd_Parse(int argc, char **argv, struct cmd_syndesc **outsyntax) { char *pname; - struct cmd_syndesc *ts; + struct cmd_syndesc *ts = NULL; struct cmd_parmdesc *tparm; afs_int32 i, j; int curType; int positional; int ambig; + int code = 0; static int initd = 0; /*Is this the first time this routine has been called? */ static int initcmdpossible = 1; /*Should be consider parsing the initial command? */ + *outsyntax = NULL; + if (!initd) { initd = 1; - /* Add help, apropos commands once */ - if (!noOpcodes) { - ts = cmd_CreateSyntax("help", HelpProc, NULL, - "get help on commands"); - cmd_AddParm(ts, "-topic", CMD_LIST, CMD_OPTIONAL, "help string"); - cmd_AddParm(ts, "-admin", CMD_FLAG, CMD_OPTIONAL, NULL); - - ts = cmd_CreateSyntax("apropos", AproposProc, NULL, - "search by help text"); - cmd_AddParm(ts, "-topic", CMD_SINGLE, CMD_REQUIRED, - "help string"); - ts = cmd_CreateSyntax("version", VersionProc, NULL, - (char *)CMD_HIDDEN); - ts = cmd_CreateSyntax("-version", VersionProc, NULL, - (char *)CMD_HIDDEN); - ts = cmd_CreateSyntax("-help", HelpProc, NULL, - (char *)CMD_HIDDEN); - ts = cmd_CreateSyntax("--version", VersionProc, NULL, - (char *)CMD_HIDDEN); - ts = cmd_CreateSyntax("--help", HelpProc, NULL, - (char *)CMD_HIDDEN); - } + initSyntax(); } /*Remember the program name */ @@ -711,17 +721,19 @@ cmd_Dispatch(int argc, char **argv) if (argc == 1) { if (!NoParmsOK(allSyntax)) { printf("%s: Type '%s -help' for help\n", pname, pname); - return (CMD_USAGE); + code = CMD_USAGE; + goto out; } } } else { if (argc < 2) { /* if there is an initcmd, don't print an error message, just * setup to use the initcmd below. */ - if (!(initcmdpossible && FindSyntax(initcmd_opcode, (int *)0))) { + if (!(initcmdpossible && FindSyntax(initcmd_opcode, NULL))) { printf("%s: Type '%s help' or '%s help ' for help\n", pname, pname, pname); - return (CMD_USAGE); + code = CMD_USAGE; + goto out; } } } @@ -738,7 +750,7 @@ cmd_Dispatch(int argc, char **argv) * see if there is a descriptor for the initialization opcode. * Only try this once. */ initcmdpossible = 0; - ts = FindSyntax(initcmd_opcode, (int *)0); + ts = FindSyntax(initcmd_opcode, NULL); if (!ts) { /*There is no initialization opcode available, so we declare * an error */ @@ -753,7 +765,8 @@ cmd_Dispatch(int argc, char **argv) "Unrecognized operation '%s'; type '%shelp' for list\n", argv[1], NName(pname, " ")); } - return (CMD_UNKNOWNCMD); + code = CMD_UNKNOWNCMD; + goto out; } else { /*Found syntax structure for an initialization opcode. Fix * up argv and argc to relect what the user @@ -762,7 +775,8 @@ cmd_Dispatch(int argc, char **argv) fprintf(stderr, "%sCan't insert implicit init opcode into command line\n", NName(pname, ": ")); - return (CMD_INTERNALERROR); + code = CMD_INTERNALERROR; + goto out; } } } /*Initial opcode not yet attempted */ @@ -779,7 +793,8 @@ cmd_Dispatch(int argc, char **argv) "Unrecognized operation '%s'; type '%shelp' for list\n", argv[1], NName(pname, " ")); } - return CMD_UNKNOWNCMD; + code = CMD_UNKNOWNCMD; + goto out; } } /*Argv[1] is not a valid opcode */ } /*Opcodes are defined */ @@ -812,14 +827,14 @@ cmd_Dispatch(int argc, char **argv) else fprintf(stderr, "'%shelp %s' for detailed help\n", NName(argv[0], " "), ts->name); - ResetSyntax(ts); - return (CMD_UNKNOWNSWITCH); + code = CMD_UNKNOWNSWITCH; + goto out; } if (j >= CMD_MAXPARMS) { fprintf(stderr, "%sInternal parsing error\n", NName(pname, ": ")); - ResetSyntax(ts); - return (CMD_INTERNALERROR); + code = CMD_INTERNALERROR; + goto out; } if (ts->parms[j].type == CMD_FLAG) { ts->parms[j].items = &dummy; @@ -832,8 +847,8 @@ cmd_Dispatch(int argc, char **argv) /* Try to fit in this descr */ if (curType >= CMD_MAXPARMS) { fprintf(stderr, "%sToo many arguments\n", NName(pname, ": ")); - ResetSyntax(ts); - return (CMD_TOOMANY); + code = CMD_TOOMANY; + goto out; } tparm = &ts->parms[curType]; @@ -855,8 +870,8 @@ cmd_Dispatch(int argc, char **argv) if (tparm->items) { fprintf(stderr, "%sToo many values after switch %s\n", NName(pname, ": "), tparm->name); - ResetSyntax(ts); - return (CMD_NOTLIST); + code = CMD_NOTLIST; + goto out; } AddItem(tparm, argv[i]); /* Add to end of list */ } else if (tparm->type == CMD_LIST) { @@ -879,8 +894,8 @@ cmd_Dispatch(int argc, char **argv) /* Display full help syntax if we don't have subcommands */ if (noOpcodes) PrintFlagHelp(ts); - ResetSyntax(ts); - return 0; + code = CMD_USAGE; + goto out; } /* Parsing done, see if we have all of our required parameters */ @@ -891,19 +906,35 @@ cmd_Dispatch(int argc, char **argv) if ((tparm->flags & CMD_PROCESSED) && tparm->items == 0) { fprintf(stderr, "%s The field '%s' isn't completed properly\n", NName(pname, ": "), tparm->name); - ResetSyntax(ts); - tparm->flags &= ~CMD_PROCESSED; - return (CMD_TOOFEW); + code = CMD_TOOFEW; + goto out; } if (!(tparm->flags & CMD_OPTIONAL) && tparm->items == 0) { fprintf(stderr, "%sMissing required parameter '%s'\n", NName(pname, ": "), tparm->name); - ResetSyntax(ts); - tparm->flags &= ~CMD_PROCESSED; - return (CMD_TOOFEW); + code = CMD_TOOFEW; + goto out; } tparm->flags &= ~CMD_PROCESSED; } + *outsyntax = ts; + +out: + if (code && ts != NULL) + ResetSyntax(ts); + + return code; +} + +int +cmd_Dispatch(int argc, char **argv) +{ + struct cmd_syndesc *ts = NULL; + int code; + + code = cmd_Parse(argc, argv, &ts); + if (code) + return code; /* * Before calling the beforeProc and afterProc and all the implications @@ -911,25 +942,33 @@ cmd_Dispatch(int argc, char **argv) * now. */ if ((ts->proc == HelpProc) || (ts->proc == AproposProc)) { - i = (*ts->proc) (ts, ts->rock); - ResetSyntax(ts); - return (i); + code = (*ts->proc) (ts, ts->rock); + goto out; } /* Now, we just call the procedure and return */ if (beforeProc) - i = (*beforeProc) (ts, beforeRock); - else - i = 0; - if (i) { - ResetSyntax(ts); - return (i); - } - i = (*ts->proc) (ts, ts->rock); + code = (*beforeProc) (ts, beforeRock); + + if (code) + goto out; + + code = (*ts->proc) (ts, ts->rock); + if (afterProc) (*afterProc) (ts, afterRock); - ResetSyntax(ts); /* Reset and free things */ - return (i); +out: + cmd_FreeOptions(&ts); + return code; +} + +void +cmd_FreeOptions(struct cmd_syndesc **ts) +{ + if (*ts != NULL) { + ResetSyntax(*ts); + *ts = NULL; + } } /* free token list returned by parseLine */ diff --git a/src/cmd/cmd.p.h b/src/cmd/cmd.p.h index 3fdd5e202..ab81211d2 100644 --- a/src/cmd/cmd.p.h +++ b/src/cmd/cmd.p.h @@ -82,4 +82,7 @@ extern void cmd_DisableAbbreviations(void); extern void PrintSyntax(struct cmd_syndesc *as); extern void PrintFlagHelp(struct cmd_syndesc *as); +extern int cmd_Parse(int argc, char **argv, struct cmd_syndesc **outsyntax); +extern void cmd_FreeOptions(struct cmd_syndesc **ts); + #endif /* __CMD_INCL__ */ diff --git a/tests/cmd/command-t.c b/tests/cmd/command-t.c index c69d38eef..ea53eaee3 100644 --- a/tests/cmd/command-t.c +++ b/tests/cmd/command-t.c @@ -53,10 +53,11 @@ main(int argc, char **argv) { char *tv[100]; struct cmd_syndesc *opts; + struct cmd_syndesc *retopts; int code; int tc; - plan(29); + plan(47); initialize_CMD_error_table(); @@ -70,6 +71,12 @@ main(int argc, char **argv) is_int(0, code, "cmd_ParseLine succeeds"); code = cmd_Dispatch(tc, tv); is_int(0, code, "dispatching simple comamnd line succeeds"); + code = cmd_Parse(tc, tv, &retopts); + is_int(0, code, "parsing simple command line succeeds"); + is_string("foo", retopts->parms[0].items->data, " ... 1st option matches"); + is_string("bar", retopts->parms[1].items->data, " ... 2nd option matches"); + ok(retopts->parms[2].items != NULL, " ... 3rd option matches"); + cmd_FreeOptions(&retopts); cmd_FreeArgv(tv); /* unknown switch */ @@ -77,6 +84,8 @@ main(int argc, char **argv) is_int(0, code, "cmd_ParseLine succeeds"); code = cmd_Dispatch(tc, tv); is_int(CMD_UNKNOWNSWITCH, code, "invalid options fail as expected"); + code = cmd_Parse(tc, tv, &retopts); + is_int(CMD_UNKNOWNSWITCH, code, "and still fail with cmd_Parse"); cmd_FreeArgv(tv); /* missing parameter */ @@ -84,6 +93,8 @@ main(int argc, char **argv) is_int(0, code, "cmd_ParseLine succeeds"); code = cmd_Dispatch(tc, tv); is_int(CMD_TOOFEW, code, "missing parameters fail as expected"); + code = cmd_Parse(tc, tv, &retopts); + is_int(CMD_TOOFEW, code, "and still fail with cmd_Parse"); cmd_FreeArgv(tv); /* missing option */ @@ -91,12 +102,16 @@ main(int argc, char **argv) is_int(0, code, "cmd_ParseLine succeeds"); code = cmd_Dispatch(tc, tv); is_int(CMD_UNKNOWNSWITCH, code, "missing options fail as expected"); + code = cmd_Parse(tc, tv, &retopts); + is_int(CMD_UNKNOWNSWITCH, code, "and still fail with cmd_Parse"); cmd_FreeArgv(tv); code = cmd_ParseLine("-first foo baz -second bar -third -flag", tv, &tc, 100); is_int(0, code, "cmd_ParseLine succeeds"); code = cmd_Dispatch(tc, tv); is_int(CMD_NOTLIST, code, "too many parameters fails as expected"); + code = cmd_Parse(tc, tv, &retopts); + is_int(CMD_NOTLIST, code, "and still fail with cmd_Parse"); cmd_FreeArgv(tv); /* Positional parameters */ @@ -104,6 +119,9 @@ main(int argc, char **argv) is_int(0, code, "cmd_ParseLine succeeds"); code = cmd_Dispatch(tc, tv); is_int(0, code, "dispatching positional parameters succeeds"); + code = cmd_Parse(tc, tv, &retopts); + is_int(0, code, "and works with cmd_Parse"); + cmd_FreeOptions(&retopts); cmd_FreeArgv(tv); /* Abbreviations */ @@ -111,6 +129,10 @@ main(int argc, char **argv) is_int(0, code, "cmd_ParseLine succeeds"); code = cmd_Dispatch(tc, tv); is_int(0, code, "dispatching abbreviations succeeds"); + code = cmd_Parse(tc, tv, &retopts); + is_int(0, code, "and works with cmd_Parse"); + cmd_FreeOptions(&retopts); + cmd_FreeArgv(tv); /* Ambiguous */ @@ -118,6 +140,8 @@ main(int argc, char **argv) is_int(0, code, "cmd_ParseLine succeeds"); code = cmd_Dispatch(tc, tv); is_int(CMD_UNKNOWNSWITCH, code, "ambiguous abbreviations correctly fail"); + code = cmd_Parse(tc, tv, &retopts); + is_int(CMD_UNKNOWNSWITCH, code, "and fail with cmd_Parse too"); cmd_FreeArgv(tv); /* Disable positional commands */ @@ -125,7 +149,9 @@ main(int argc, char **argv) code = cmd_ParseLine("foo bar -flag", tv, &tc, 100); is_int(0, code, "cmd_ParseLine succeeds"); code = cmd_Dispatch(tc, tv); - is_int(3359746, code, "positional parameters can be disabled"); + is_int(CMD_NOTLIST, code, "positional parameters can be disabled"); + code = cmd_Parse(tc, tv, &retopts); + is_int(CMD_NOTLIST, code, "and fail with cmd_Parse too"); cmd_FreeArgv(tv); /* Disable abbreviations */ @@ -134,6 +160,21 @@ main(int argc, char **argv) is_int(0, code, "cmd_ParseLine succeeds"); code = cmd_Dispatch(tc, tv); is_int(CMD_UNKNOWNSWITCH, code, "dispatching abbreviations succeeds"); + code = cmd_Parse(tc, tv, &retopts); + is_int(CMD_UNKNOWNSWITCH, code, "and fail with cmd_Parse too"); + + cmd_FreeArgv(tv); + + /* Try the new cmd_Parse function with something different*/ + code = cmd_ParseLine("-first one -second two -flag", tv, &tc, 100); + is_int(0, code, "cmd_ParseLine succeeds"); + code = cmd_Parse(tc, tv, &retopts); + is_int(0, code, "Parsing with cmd_Parse works"); + is_string("one", retopts->parms[0].items->data, " ... 1st option matches"); + is_string("two", retopts->parms[1].items->data, " ... 2nd option matches"); + ok(retopts->parms[2].items != NULL, " ... 3rd option matches"); + + cmd_FreeOptions(&retopts); cmd_FreeArgv(tv); return 0; -- 2.39.5