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>
11 #include <jim-subcmd.h>
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 */
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
)
33 for (; ct
->cmd
; ct
++) {
34 if (!(ct
->flags
& JIM_MODFLAG_HIDDEN
)) {
35 Jim_AppendStrings(interp
, Jim_GetResult(interp
), s
, ct
->cmd
, NULL
);
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_SetResultFormatted(interp
, "%#s, %s command \"%#s\": should be ", cmd
, type
, subcmd
);
45 add_commands(interp
, command_table
, ", ");
48 static void show_cmd_usage(Jim_Interp
*interp
, const jim_subcmd_type
* command_table
, int argc
,
51 Jim_SetResultFormatted(interp
, "Usage: \"%#s command ... \", where command is one of: ", argv
[0]);
52 add_commands(interp
, command_table
, ", ");
55 static void add_cmd_usage(Jim_Interp
*interp
, const jim_subcmd_type
* ct
, Jim_Obj
*cmd
)
58 Jim_AppendStrings(interp
, Jim_GetResult(interp
), Jim_String(cmd
), " ", NULL
);
60 Jim_AppendStrings(interp
, Jim_GetResult(interp
), ct
->cmd
, NULL
);
61 if (ct
->args
&& *ct
->args
) {
62 Jim_AppendStrings(interp
, Jim_GetResult(interp
), " ", ct
->args
, NULL
);
66 static void set_wrong_args(Jim_Interp
*interp
, const jim_subcmd_type
* command_table
, Jim_Obj
*subcmd
)
68 Jim_SetResultString(interp
, "wrong # args: should be \"", -1);
69 add_cmd_usage(interp
, command_table
, subcmd
);
70 Jim_AppendStrings(interp
, Jim_GetResult(interp
), "\"", NULL
);
73 /* internal rep is stored in ptrIntvalue
77 static const Jim_ObjType subcmdLookupObjType
= {
85 const jim_subcmd_type
*Jim_ParseSubCmd(Jim_Interp
*interp
, const jim_subcmd_type
* command_table
,
86 int argc
, Jim_Obj
*const *argv
)
88 const jim_subcmd_type
*ct
;
89 const jim_subcmd_type
*partial
= 0;
96 Jim_SetResultFormatted(interp
, "wrong # args: should be \"%#s command ...\"\n"
97 "Use \"%#s -help ?command?\" for help", argv
[0], argv
[0]);
103 /* Use cached lookup if possible */
104 if (cmd
->typePtr
== &subcmdLookupObjType
) {
105 if (cmd
->internalRep
.ptrIntValue
.ptr
== command_table
) {
106 ct
= command_table
+ cmd
->internalRep
.ptrIntValue
.int1
;
111 /* Check for the help command */
112 if (Jim_CompareStringImmediate(interp
, cmd
, "-help")) {
114 /* Usage for the command, not the subcommand */
115 show_cmd_usage(interp
, command_table
, argc
, argv
);
116 return &dummy_subcmd
;
120 /* Skip the 'help' command */
124 /* Check for special builtin '-commands' command first */
125 if (Jim_CompareStringImmediate(interp
, cmd
, "-commands")) {
126 /* Build the result here */
127 Jim_SetResult(interp
, Jim_NewEmptyStringObj(interp
));
128 add_commands(interp
, command_table
, " ");
129 return &dummy_subcmd
;
132 cmdstr
= Jim_GetString(cmd
, &cmdlen
);
134 for (ct
= command_table
; ct
->cmd
; ct
++) {
135 if (Jim_CompareStringImmediate(interp
, cmd
, ct
->cmd
)) {
136 /* Found an exact match */
139 if (strncmp(cmdstr
, ct
->cmd
, cmdlen
) == 0) {
143 /* Just show the top level help here */
144 show_cmd_usage(interp
, command_table
, argc
, argv
);
145 return &dummy_subcmd
;
147 bad_subcmd(interp
, command_table
, "ambiguous", argv
[0], argv
[1 + help
]);
155 /* If we had an unambiguous partial match */
156 if (partial
&& !ct
->cmd
) {
161 /* No matching command */
163 /* Just show the top level help here */
164 show_cmd_usage(interp
, command_table
, argc
, argv
);
165 return &dummy_subcmd
;
167 bad_subcmd(interp
, command_table
, "unknown", argv
[0], argv
[1 + help
]);
172 Jim_SetResultString(interp
, "Usage: ", -1);
174 add_cmd_usage(interp
, ct
, argv
[0]);
175 return &dummy_subcmd
;
178 /* Cache the result for a successful non-help lookup */
179 Jim_FreeIntRep(interp
, cmd
);
180 cmd
->typePtr
= &subcmdLookupObjType
;
181 cmd
->internalRep
.ptrIntValue
.ptr
= (void *)command_table
;
182 cmd
->internalRep
.ptrIntValue
.int1
= ct
- command_table
;
185 /* Check the number of args */
186 if (argc
- 2 < ct
->minargs
|| (ct
->maxargs
>= 0 && argc
- 2 > ct
->maxargs
)) {
187 Jim_SetResultString(interp
, "wrong # args: should be \"", -1);
189 add_cmd_usage(interp
, ct
, argv
[0]);
190 Jim_AppendStrings(interp
, Jim_GetResult(interp
), "\"", NULL
);
199 int Jim_CallSubCmd(Jim_Interp
*interp
, const jim_subcmd_type
* ct
, int argc
, Jim_Obj
*const *argv
)
204 if (ct
->flags
& JIM_MODFLAG_FULLARGV
) {
205 ret
= ct
->function(interp
, argc
, argv
);
208 ret
= ct
->function(interp
, argc
- 2, argv
+ 2);
211 set_wrong_args(interp
, ct
, argv
[0]);
218 int Jim_SubCmdProc(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
220 const jim_subcmd_type
*ct
=
221 Jim_ParseSubCmd(interp
, (const jim_subcmd_type
*)Jim_CmdPrivData(interp
), argc
, argv
);
223 return Jim_CallSubCmd(interp
, ct
, argc
, argv
);