zlib: Don't use PASTE for INTMAX error messages
[jimtcl.git] / jim-subcmd.c
blobc5228309628323f94bd69195c9fb90ca7865cb9c
1 /*
2 * Makes it easy to support "ensembles". i.e. commands with subcommands
3 * like [string] and [array]
5 * (c) 2008 Steve Bennett <steveb@workware.net.au>
7 */
8 #include <stdio.h>
9 #include <string.h>
11 #include <jim-subcmd.h>
13 /**
14 * Implements the common 'commands' subcommand
16 static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
18 /* Nothing to do, since the result has already been created */
19 return JIM_OK;
22 /**
23 * Do-nothing command to support -commands and -usage
25 static const jim_subcmd_type dummy_subcmd = {
26 "dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN
29 static void add_commands(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep)
31 const char *s = "";
33 for (; ct->cmd; ct++) {
34 if (!(ct->flags & JIM_MODFLAG_HIDDEN)) {
35 Jim_AppendStrings(interp, Jim_GetResult(interp), s, ct->cmd, NULL);
36 s = sep;
41 static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type,
42 Jim_Obj *cmd, Jim_Obj *subcmd)
44 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
45 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), ", ", type,
46 " command \"", Jim_String(subcmd), "\": should be ", NULL);
47 add_commands(interp, command_table, ", ");
50 static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc,
51 Jim_Obj *const *argv)
53 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
54 Jim_AppendStrings(interp, Jim_GetResult(interp), "Usage: \"", Jim_String(argv[0]),
55 " command ... \", where command is one of: ", NULL);
56 add_commands(interp, command_table, ", ");
59 static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd)
61 if (cmd) {
62 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL);
64 Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL);
65 if (ct->args && *ct->args) {
66 Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL);
70 static void set_wrong_args(Jim_Interp *interp, const jim_subcmd_type * command_table, Jim_Obj *subcmd)
72 Jim_SetResultString(interp, "wrong # args: should be \"", -1);
73 add_cmd_usage(interp, command_table, subcmd);
74 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
77 const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table,
78 int argc, Jim_Obj *const *argv)
80 const jim_subcmd_type *ct;
81 const jim_subcmd_type *partial = 0;
82 int cmdlen;
83 Jim_Obj *cmd;
84 const char *cmdstr;
85 const char *cmdname;
86 int help = 0;
88 cmdname = Jim_String(argv[0]);
90 if (argc < 2) {
91 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
92 Jim_AppendStrings(interp, Jim_GetResult(interp), "wrong # args: should be \"", cmdname,
93 " command ...\"\n", NULL);
94 Jim_AppendStrings(interp, Jim_GetResult(interp), "Use \"", cmdname, " -help ?command?\" for help", NULL);
95 return 0;
98 cmd = argv[1];
100 /* Check for the help command */
101 if (Jim_CompareStringImmediate(interp, cmd, "-help")) {
102 if (argc == 2) {
103 /* Usage for the command, not the subcommand */
104 show_cmd_usage(interp, command_table, argc, argv);
105 return &dummy_subcmd;
107 help = 1;
109 /* Skip the 'help' command */
110 cmd = argv[2];
113 /* Check for special builtin '-commands' command first */
114 if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
115 /* Build the result here */
116 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
117 add_commands(interp, command_table, " ");
118 return &dummy_subcmd;
121 cmdstr = Jim_GetString(cmd, &cmdlen);
123 for (ct = command_table; ct->cmd; ct++) {
124 if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) {
125 /* Found an exact match */
126 break;
128 if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) {
129 if (partial) {
130 /* Ambiguous */
131 if (help) {
132 /* Just show the top level help here */
133 show_cmd_usage(interp, command_table, argc, argv);
134 return &dummy_subcmd;
136 bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]);
137 return 0;
139 partial = ct;
141 continue;
144 /* If we had an unambiguous partial match */
145 if (partial && !ct->cmd) {
146 ct = partial;
149 if (!ct->cmd) {
150 /* No matching command */
151 if (help) {
152 /* Just show the top level help here */
153 show_cmd_usage(interp, command_table, argc, argv);
154 return &dummy_subcmd;
156 bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]);
157 return 0;
160 if (help) {
161 Jim_SetResultString(interp, "Usage: ", -1);
162 /* subcmd */
163 add_cmd_usage(interp, ct, argv[0]);
164 return &dummy_subcmd;
167 /* Check the number of args */
168 if (argc - 2 < ct->minargs || (ct->maxargs >= 0 && argc - 2 > ct->maxargs)) {
169 Jim_SetResultString(interp, "wrong # args: should be \"", -1);
170 /* subcmd */
171 add_cmd_usage(interp, ct, argv[0]);
172 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
174 return 0;
177 /* Good command */
178 return ct;
181 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv)
183 int ret = JIM_ERR;
185 if (ct) {
186 if (ct->flags & JIM_MODFLAG_FULLARGV) {
187 ret = ct->function(interp, argc, argv);
189 else {
190 ret = ct->function(interp, argc - 2, argv + 2);
192 if (ret < 0) {
193 set_wrong_args(interp, ct, argv[0]);
194 ret = JIM_ERR;
197 return ret;
200 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
202 const jim_subcmd_type *ct =
203 Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv);
205 return Jim_CallSubCmd(interp, ct, argc, argv);