aboutsummaryrefslogtreecommitdiff
path: root/jim-subcmd.c
diff options
context:
space:
mode:
authorSteve Bennett <steveb@workware.net.au>2009-07-27 10:44:46 +1000
committerSteve Bennett <steveb@workware.net.au>2010-10-15 10:11:01 +1000
commit63011a2556161f26605f125eac5b9f25676112a7 (patch)
tree3838e67318740921868e2ad18bb50192df33eb9f /jim-subcmd.c
parent7f683b890f5575999bfae65988079c227735822d (diff)
downloadjimtcl-63011a2556161f26605f125eac5b9f25676112a7.zip
jimtcl-63011a2556161f26605f125eac5b9f25676112a7.tar.gz
jimtcl-63011a2556161f26605f125eac5b9f25676112a7.tar.bz2
Add file and exec (along with subcmd support)
Diffstat (limited to 'jim-subcmd.c')
-rw-r--r--jim-subcmd.c194
1 files changed, 194 insertions, 0 deletions
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 <stdio.h>
+#include <string.h>
+
+#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);
+}