load: use AssocData to free load handles
[jimtcl.git] / jim-subcmd.c
blobbd6e0506e48e239f940ae4aaac6cf67095074eef
1 #include <stdio.h>
2 #include <string.h>
4 #include <jim-subcmd.h>
6 /**
7 * Implements the common 'commands' subcommand
8 */
9 static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
11 /* Nothing to do, since the result has already been created */
12 return JIM_OK;
15 /**
16 * Do-nothing command to support -commands and -usage
18 static const jim_subcmd_type dummy_subcmd = {
19 "dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN
22 static void add_commands(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep)
24 const char *s = "";
26 for (; ct->cmd; ct++) {
27 if (!(ct->flags & JIM_MODFLAG_HIDDEN)) {
28 Jim_AppendStrings(interp, Jim_GetResult(interp), s, ct->cmd, NULL);
29 s = sep;
34 static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type,
35 Jim_Obj *cmd, Jim_Obj *subcmd)
37 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
38 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), ", ", type,
39 " command \"", Jim_String(subcmd), "\": should be ", NULL);
40 add_commands(interp, command_table, ", ");
43 static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc,
44 Jim_Obj *const *argv)
46 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
47 Jim_AppendStrings(interp, Jim_GetResult(interp), "Usage: \"", Jim_String(argv[0]),
48 " command ... \", where command is one of: ", NULL);
49 add_commands(interp, command_table, ", ");
52 static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd)
54 if (cmd) {
55 Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL);
57 Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL);
58 if (ct->args && *ct->args) {
59 Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL);
63 static void set_wrong_args(Jim_Interp *interp, const jim_subcmd_type * command_table, Jim_Obj *subcmd)
65 Jim_SetResultString(interp, "wrong # args: should be \"", -1);
66 add_cmd_usage(interp, command_table, subcmd);
67 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
70 const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table,
71 int argc, Jim_Obj *const *argv)
73 const jim_subcmd_type *ct;
74 const jim_subcmd_type *partial = 0;
75 int cmdlen;
76 Jim_Obj *cmd;
77 const char *cmdstr;
78 const char *cmdname;
79 int help = 0;
81 cmdname = Jim_String(argv[0]);
83 if (argc < 2) {
84 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
85 Jim_AppendStrings(interp, Jim_GetResult(interp), "wrong # args: should be \"", cmdname,
86 " command ...\"\n", NULL);
87 Jim_AppendStrings(interp, Jim_GetResult(interp), "Use \"", cmdname, " -help ?command?\" for help", NULL);
88 return 0;
91 cmd = argv[1];
93 /* Check for the help command */
94 if (Jim_CompareStringImmediate(interp, cmd, "-help")) {
95 if (argc == 2) {
96 /* Usage for the command, not the subcommand */
97 show_cmd_usage(interp, command_table, argc, argv);
98 return &dummy_subcmd;
100 help = 1;
102 /* Skip the 'help' command */
103 cmd = argv[2];
106 /* Check for special builtin '-commands' command first */
107 if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
108 /* Build the result here */
109 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
110 add_commands(interp, command_table, " ");
111 return &dummy_subcmd;
114 cmdstr = Jim_GetString(cmd, &cmdlen);
116 for (ct = command_table; ct->cmd; ct++) {
117 if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) {
118 /* Found an exact match */
119 break;
121 if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) {
122 if (partial) {
123 /* Ambiguous */
124 if (help) {
125 /* Just show the top level help here */
126 show_cmd_usage(interp, command_table, argc, argv);
127 return &dummy_subcmd;
129 bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]);
130 return 0;
132 partial = ct;
134 continue;
137 /* If we had an unambiguous partial match */
138 if (partial && !ct->cmd) {
139 ct = partial;
142 if (!ct->cmd) {
143 /* No matching command */
144 if (help) {
145 /* Just show the top level help here */
146 show_cmd_usage(interp, command_table, argc, argv);
147 return &dummy_subcmd;
149 bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]);
150 return 0;
153 if (help) {
154 Jim_SetResultString(interp, "Usage: ", -1);
155 /* subcmd */
156 add_cmd_usage(interp, ct, argv[0]);
157 return &dummy_subcmd;
160 /* Check the number of args */
161 if (argc - 2 < ct->minargs || (ct->maxargs >= 0 && argc - 2 > ct->maxargs)) {
162 Jim_SetResultString(interp, "wrong # args: should be \"", -1);
163 /* subcmd */
164 add_cmd_usage(interp, ct, argv[0]);
165 Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
167 return 0;
170 /* Good command */
171 return ct;
174 int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv)
176 int ret = JIM_ERR;
178 if (ct) {
179 if (ct->flags & JIM_MODFLAG_FULLARGV) {
180 ret = ct->function(interp, argc, argv);
182 else {
183 ret = ct->function(interp, argc - 2, argv + 2);
185 if (ret < 0) {
186 set_wrong_args(interp, ct, argv[0]);
187 ret = JIM_ERR;
190 return ret;
193 int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
195 const jim_subcmd_type *ct =
196 Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv);
198 return Jim_CallSubCmd(interp, ct, argc, argv);