Add a general purpose hashtable pattern matcher
[jimtcl.git] / jim-subcmd.c
blobf417c1edea813b31e01ebb96a0c21c16c03c8add
1 #include <stdio.h>
2 #include <string.h>
4 #include "jim-subcmd.h"
5 #include "jimautoconf.h"
7 /**
8 * Implements the common 'commands' subcommand
9 */
10 static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
12 /* Nothing to do, since the result has already been created */
13 return JIM_OK;
16 /**
17 * Do-nothing command to support -commands and -usage
19 static const jim_subcmd_type dummy_subcmd = {
20 "dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN
23 static void add_commands(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep)
25 const char *s = "";
27 for (; ct->cmd; ct++) {
28 if (!(ct->flags & JIM_MODFLAG_HIDDEN)) {
29 Jim_AppendStrings(interp, Jim_GetResult(interp), s, ct->cmd, NULL);
30 s = sep;
35 static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type,
36 Jim_Obj *cmd, Jim_Obj *subcmd)
38 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
39 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), ", ", type,
40 " command \"", Jim_String(subcmd), "\": should be ", NULL);
41 add_commands(interp, command_table, ", ");
44 static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc,
45 Jim_Obj *const *argv)
47 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
48 Jim_AppendStrings(interp, Jim_GetResult(interp), "Usage: \"", Jim_String(argv[0]),
49 " command ... \", where command is one of: ", NULL);
50 add_commands(interp, command_table, ", ");
53 static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd)
55 if (cmd) {
56 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL);
58 Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL);
59 if (ct->args && *ct->args) {
60 Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL);
64 static void set_wrong_args(Jim_Interp *interp, const jim_subcmd_type * command_table, Jim_Obj *subcmd)
66 Jim_SetResultString(interp, "wrong # args: must be \"", -1);
67 add_cmd_usage(interp, command_table, subcmd);
68 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
71 const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table,
72 int argc, Jim_Obj *const *argv)
74 const jim_subcmd_type *ct;
75 const jim_subcmd_type *partial = 0;
76 int cmdlen;
77 Jim_Obj *cmd;
78 const char *cmdstr;
79 const char *cmdname;
80 int help = 0;
82 cmdname = Jim_String(argv[0]);
84 if (argc < 2) {
85 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
86 Jim_AppendStrings(interp, Jim_GetResult(interp), "wrong # args: should be \"", cmdname,
87 " command ...\"\n", NULL);
88 Jim_AppendStrings(interp, Jim_GetResult(interp), "Use \"", cmdname, " -help ?command?\" for help", NULL);
89 return 0;
92 cmd = argv[1];
94 /* Check for the help command */
95 if (Jim_CompareStringImmediate(interp, cmd, "-help")) {
96 if (argc == 2) {
97 /* Usage for the command, not the subcommand */
98 show_cmd_usage(interp, command_table, argc, argv);
99 return &dummy_subcmd;
101 help = 1;
103 /* Skip the 'help' command */
104 cmd = argv[2];
107 /* Check for special builtin '-commands' command first */
108 if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
109 /* Build the result here */
110 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
111 add_commands(interp, command_table, " ");
112 return &dummy_subcmd;
115 cmdstr = Jim_GetString(cmd, &cmdlen);
117 for (ct = command_table; ct->cmd; ct++) {
118 if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) {
119 /* Found an exact match */
120 break;
122 if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) {
123 if (partial) {
124 /* Ambiguous */
125 if (help) {
126 /* Just show the top level help here */
127 show_cmd_usage(interp, command_table, argc, argv);
128 return &dummy_subcmd;
130 bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]);
131 return 0;
133 partial = ct;
135 continue;
138 /* If we had an unambiguous partial match */
139 if (partial && !ct->cmd) {
140 ct = partial;
143 if (!ct->cmd) {
144 /* No matching command */
145 if (help) {
146 /* Just show the top level help here */
147 show_cmd_usage(interp, command_table, argc, argv);
148 return &dummy_subcmd;
150 bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]);
151 return 0;
154 if (help) {
155 Jim_SetResultString(interp, "Usage: ", -1);
156 /* subcmd */
157 add_cmd_usage(interp, ct, argv[0]);
158 return &dummy_subcmd;
161 /* Check the number of args */
162 if (argc - 2 < ct->minargs || (ct->maxargs >= 0 && argc - 2 > ct->maxargs)) {
163 Jim_SetResultString(interp, "wrong # args: must be \"", -1);
164 /* subcmd */
165 add_cmd_usage(interp, ct, argv[0]);
166 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
168 return 0;
171 /* Good command */
172 return ct;
175 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv)
177 int ret = JIM_ERR;
179 if (ct) {
180 if (ct->flags & JIM_MODFLAG_FULLARGV) {
181 ret = ct->function(interp, argc, argv);
183 else {
184 ret = ct->function(interp, argc - 2, argv + 2);
186 if (ret < 0) {
187 set_wrong_args(interp, ct, argv[0]);
188 ret = JIM_ERR;
191 return ret;
194 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
196 const jim_subcmd_type *ct =
197 Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv);
199 return Jim_CallSubCmd(interp, ct, argc, argv);