From 63011a2556161f26605f125eac5b9f25676112a7 Mon Sep 17 00:00:00 2001 From: Steve Bennett Date: Mon, 27 Jul 2009 10:44:46 +1000 Subject: Add file and exec (along with subcmd support) --- jim-subcmd.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 jim-subcmd.c (limited to 'jim-subcmd.c') diff --git a/jim-subcmd.c b/jim-subcmd.c new file mode 100644 index 0000000..7c2ce03 --- /dev/null +++ b/jim-subcmd.c @@ -0,0 +1,194 @@ +#include +#include + +#include "jim-subcmd.h" + +/** + * Implements the common 'commands' subcommand + */ +static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + /* Nothing to do, since the result has already been created */ + return JIM_OK; +} + +/** + * Do-nothing command to support -commands and -usage + */ +static const jim_subcmd_type dummy_subcmd = { + .cmd = "dummy", + .function = subcmd_null, + .flags = JIM_MODFLAG_HIDDEN, +}; + +static void add_commands(Jim_Interp *interp, const jim_subcmd_type *ct, const char *sep) +{ + const char *s = ""; + + for (; ct->cmd; ct++) { + if (!(ct->flags & JIM_MODFLAG_HIDDEN)) { + Jim_AppendStrings(interp, Jim_GetResult(interp), s, ct->cmd, 0); + s = sep; + } + } +} + +static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type *command_table, const char *type, int argc, Jim_Obj *const *argv) +{ + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_GetString(argv[0], NULL), ", ", type, " command \"", Jim_GetString(argv[1], NULL), "\": should be ", NULL); + add_commands(interp, command_table, ", "); +} + +static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv) +{ + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings(interp, Jim_GetResult(interp), "Usage: \"", Jim_GetString(argv[0], NULL), " command ... \", where command is one of: ", NULL); + add_commands(interp, command_table, ", "); +} + +static void add_subcmd_usage(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv) +{ + Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_GetString(argv[0], NULL), " ", ct->cmd, NULL); + + if (ct->args && *ct->args) { + Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL); + } +} + +static void show_full_usage(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv) +{ + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + for (; ct->cmd; ct++) { + if (!(ct->flags & JIM_MODFLAG_HIDDEN)) { + add_subcmd_usage(interp, ct, argc, argv); + if (ct->description) { + Jim_AppendStrings(interp, Jim_GetResult(interp), "\n\n ", ct->description, 0); + } + Jim_AppendStrings(interp, Jim_GetResult(interp), "\n\n", 0); + } + } +} + +const jim_subcmd_type * +Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv) +{ + const jim_subcmd_type *ct; + const jim_subcmd_type *partial = 0; + int cmdlen; + Jim_Obj *cmd; + const char *cmdstr; + const char *cmdname; + int help = 0; + + cmdname = Jim_GetString(argv[0], NULL); + + if (argc < 2) { + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings(interp, Jim_GetResult(interp), "wrong # args: should be \"", cmdname, " command ...\"\n", 0); + Jim_AppendStrings(interp, Jim_GetResult(interp), "Use \"", cmdname, " -help\" or \"", cmdname, " -help command\" for help", 0); + return 0; + } + + cmd = argv[1]; + + if (argc == 2 && Jim_CompareStringImmediate(interp, cmd, "-usage")) { + /* Show full usage */ + show_full_usage(interp, command_table, argc, argv); + return &dummy_subcmd; + } + + /* Check for the help command */ + if (Jim_CompareStringImmediate(interp, cmd, "-help")) { + if (argc == 2) { + /* Usage for the command, not the subcommand */ + show_cmd_usage(interp, command_table, argc, argv); + return 0; + } + help = 1; + + /* Skip the 'help' command */ + cmd = argv[2]; + } + + /* Check for special builtin '-commands' command first */ + if (Jim_CompareStringImmediate(interp, cmd, "-commands")) { + /* Build the result here */ + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + add_commands(interp, command_table, " "); + return &dummy_subcmd; + } + + cmdstr = Jim_GetString(cmd, &cmdlen); + + for (ct = command_table; ct->cmd; ct++) { + if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) { + /* Found an exact match */ + break; + } + if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) { + if (partial) { + /* Ambiguous */ + bad_subcmd(interp, command_table, "ambiguous", argc, argv); + return 0; + } + partial = ct; + } + continue; + } + + /* If we had an unambiguous partial match */ + if (partial && !ct->cmd) { + ct = partial; + } + + if (!ct->cmd) { + /* No matching command */ + bad_subcmd(interp, command_table, "unknown", argc, argv); + return 0; + } + + if (help) { + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + add_subcmd_usage(interp, ct, argc, argv); + if (ct->description) { + Jim_AppendStrings(interp, Jim_GetResult(interp), "\n\n", ct->description, 0); + } + return 0; + } + + /* Check the number of args */ + if (argc - 2 < ct->minargs || (ct->maxargs >= 0 && argc - 2> ct->maxargs)) { + Jim_SetResultString(interp, "wrong # args: should be \"", -1); + add_subcmd_usage(interp, ct, argc, argv); + Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL); + + return 0; + } + + /* Good command */ + return ct; +} + +int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv) +{ + int ret = JIM_ERR; + + if (ct) { + ret = ct->function(interp, argc - 2, argv + 2); + if (ret < 0) { + Jim_SetResultString(interp, "wrong # args: should be \"", -1); + add_subcmd_usage(interp, ct, argc, argv); + Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL); + ret = JIM_ERR; + } + } + return ret; +} + +int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv); + + return Jim_CallSubCmd(interp, ct, argc, argv); +} -- cgit v1.1