2 Command line processing
4 Copyright (C) Amitay Isaacs 2018
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>.
26 #include "lib/util/debug.h"
28 #include "common/cmdline.h"
30 #define CMDLINE_MAX_LEN 80
32 struct cmdline_context
{
34 struct poptOption
*options
;
35 struct cmdline_command
*commands
;
40 struct cmdline_command
*match_cmd
;
43 static bool cmdline_show_help
= false;
45 static void cmdline_popt_help(poptContext pc
,
46 enum poptCallbackReason reason
,
47 struct poptOption
*key
,
51 if (key
->shortName
== 'h') {
52 cmdline_show_help
= true;
56 struct poptOption cmdline_help_options
[] = {
57 { NULL
, '\0', POPT_ARG_CALLBACK
, cmdline_popt_help
, 0, NULL
, NULL
},
58 { "help", 'h', 0, NULL
, 'h', "Show this help message", NULL
},
62 #define CMDLINE_HELP_OPTIONS \
63 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, cmdline_help_options, \
64 0, "Help Options:", NULL }
66 static bool cmdline_option_check(struct poptOption
*option
)
68 if (option
->longName
== NULL
) {
69 D_ERR("Option has no long name\n");
73 if (option
->argInfo
!= POPT_ARG_STRING
&&
74 option
->argInfo
!= POPT_ARG_INT
&&
75 option
->argInfo
!= POPT_ARG_LONG
&&
76 option
->argInfo
!= POPT_ARG_VAL
&&
77 option
->argInfo
!= POPT_ARG_FLOAT
&&
78 option
->argInfo
!= POPT_ARG_DOUBLE
) {
79 D_ERR("Option '%s' has unsupported type\n", option
->longName
);
83 if (option
->arg
== NULL
) {
84 D_ERR("Option '%s' has invalid arg\n", option
->longName
);
88 if (option
->descrip
== NULL
) {
89 D_ERR("Option '%s' has no help msg\n", option
->longName
);
96 static bool cmdline_options_check(struct poptOption
*options
)
101 if (options
== NULL
) {
106 while (options
[i
].longName
!= NULL
|| options
[i
].shortName
!= '\0') {
107 ok
= cmdline_option_check(&options
[i
]);
117 static int cmdline_options_define(TALLOC_CTX
*mem_ctx
,
118 struct poptOption
*user_options
,
119 struct poptOption
**result
)
121 struct poptOption
*options
;
124 count
= (user_options
== NULL
? 2 : 3);
126 options
= talloc_array(mem_ctx
, struct poptOption
, count
);
127 if (options
== NULL
) {
132 options
[i
++] = (struct poptOption
) CMDLINE_HELP_OPTIONS
;
133 if (user_options
!= NULL
) {
134 options
[i
++] = (struct poptOption
) {
135 .argInfo
= POPT_ARG_INCLUDE_TABLE
,
137 .descrip
= "Options:",
140 options
[i
++] = (struct poptOption
) POPT_TABLEEND
;
146 static bool cmdline_command_check(struct cmdline_command
*cmd
, int *max_len
)
150 if (cmd
->name
== NULL
) {
154 if (cmd
->fn
== NULL
) {
155 D_ERR("Command '%s' has no implementation function\n",
160 if (cmd
->msg_help
== NULL
) {
161 D_ERR("Command '%s' has no help msg\n", cmd
->name
);
165 len
= strlen(cmd
->name
);
166 if (cmd
->msg_args
!= NULL
) {
167 len
+= strlen(cmd
->msg_args
);
169 if (len
> CMDLINE_MAX_LEN
) {
170 D_ERR("Command '%s' is too long (%zu)\n", cmd
->name
, len
);
174 if (len
> *max_len
) {
178 len
= strlen(cmd
->msg_help
);
179 if (len
> CMDLINE_MAX_LEN
) {
180 D_ERR("Command '%s' help too long (%zu)\n", cmd
->name
, len
);
187 static bool cmdline_commands_check(struct cmdline_command
*commands
,
193 if (commands
== NULL
) {
197 for (i
=0; commands
[i
].name
!= NULL
; i
++) {
198 ok
= cmdline_command_check(&commands
[i
], max_len
);
207 static int cmdline_context_destructor(struct cmdline_context
*cmdline
);
209 int cmdline_init(TALLOC_CTX
*mem_ctx
,
211 struct poptOption
*options
,
212 struct cmdline_command
*commands
,
213 struct cmdline_context
**result
)
215 struct cmdline_context
*cmdline
;
216 int ret
, max_len
= 0;
223 ok
= cmdline_options_check(options
);
228 ok
= cmdline_commands_check(commands
, &max_len
);
233 cmdline
= talloc_zero(mem_ctx
, struct cmdline_context
);
234 if (cmdline
== NULL
) {
238 cmdline
->prog
= talloc_strdup(cmdline
, prog
);
239 if (cmdline
->prog
== NULL
) {
240 talloc_free(cmdline
);
244 ret
= cmdline_options_define(cmdline
, options
, &cmdline
->options
);
246 talloc_free(cmdline
);
249 cmdline
->commands
= commands
;
250 cmdline
->max_len
= max_len
;
253 cmdline
->argv
= talloc_array(cmdline
, const char *, 2);
254 if (cmdline
->argv
== NULL
) {
255 talloc_free(cmdline
);
258 cmdline
->argv
[0] = cmdline
->prog
;
259 cmdline
->argv
[1] = NULL
;
261 /* Dummy popt context for generating help */
262 cmdline
->pc
= poptGetContext(cmdline
->prog
,
267 if (cmdline
->pc
== NULL
) {
268 talloc_free(cmdline
);
272 talloc_set_destructor(cmdline
, cmdline_context_destructor
);
278 static int cmdline_context_destructor(struct cmdline_context
*cmdline
)
280 if (cmdline
->pc
!= NULL
) {
281 poptFreeContext(cmdline
->pc
);
287 static int cmdline_parse_options(struct cmdline_context
*cmdline
,
293 if (cmdline
->pc
!= NULL
) {
294 poptFreeContext(cmdline
->pc
);
297 cmdline
->pc
= poptGetContext(cmdline
->prog
,
302 if (cmdline
->pc
== NULL
) {
306 while ((opt
= poptGetNextOpt(cmdline
->pc
)) != -1) {
307 D_ERR("Invalid option %s: %s\n",
308 poptBadOption(cmdline
->pc
, 0),
313 /* Set up remaining arguments for commands */
315 cmdline
->argv
= poptGetArgs(cmdline
->pc
);
316 if (cmdline
->argv
!= NULL
) {
317 while (cmdline
->argv
[cmdline
->argc
] != NULL
) {
325 static int cmdline_match(struct cmdline_context
*cmdline
)
329 if (cmdline
->argc
== 0 || cmdline
->argv
== NULL
) {
330 cmdline
->match_cmd
= NULL
;
334 for (i
=0; cmdline
->commands
[i
].name
!= NULL
; i
++) {
335 struct cmdline_command
*cmd
;
336 char name
[CMDLINE_MAX_LEN
+1];
342 cmd
= &cmdline
->commands
[i
];
343 len
= strlcpy(name
, cmd
->name
, sizeof(name
));
344 if (len
>= sizeof(name
)) {
345 D_ERR("Skipping long command '%s'\n", cmd
->name
);
350 while ((t
= strtok(str
, " ")) != NULL
) {
351 if (n
>= cmdline
->argc
) {
355 if (cmdline
->argv
[n
] == NULL
) {
359 if (strcmp(cmdline
->argv
[n
], t
) == 0) {
372 cmdline
->match_cmd
= cmd
;
377 cmdline
->match_cmd
= NULL
;
381 int cmdline_parse(struct cmdline_context
*cmdline
,
392 cmdline_show_help
= false;
395 ret
= cmdline_parse_options(cmdline
, argc
, argv
);
400 cmdline
->argc
= argc
;
401 cmdline
->argv
= argv
;
404 ret
= cmdline_match(cmdline
);
405 if (!cmdline_show_help
&& ret
!= 0) {
412 static void cmdline_usage_command(struct cmdline_context
*cmdline
,
413 struct cmdline_command
*cmd
,
418 len
= (int)strlen(cmd
->name
);
420 printf(" %s ", cmd
->name
);
423 cmdline
->max_len
-len
,
424 cmd
->msg_args
== NULL
? "" : cmd
->msg_args
);
426 printf("%s", cmd
->msg_args
== NULL
? "" : cmd
->msg_args
);
428 printf(" %s\n", cmd
->msg_help
);
431 static void cmdline_usage_full(struct cmdline_context
*cmdline
)
435 poptSetOtherOptionHelp(cmdline
->pc
, "[<options>] <command> [<args>]");
436 poptPrintHelp(cmdline
->pc
, stdout
, 0);
438 printf("\nCommands:\n");
439 for (i
=0; cmdline
->commands
[i
].name
!= NULL
; i
++) {
440 cmdline_usage_command(cmdline
, &cmdline
->commands
[i
], true);
445 void cmdline_usage(struct cmdline_context
*cmdline
, const char *cmd_name
)
447 struct cmdline_command
*cmd
= NULL
;
450 if (cmd_name
== NULL
) {
451 cmdline_usage_full(cmdline
);
455 for (i
=0; cmdline
->commands
[i
].name
!= NULL
; i
++) {
456 if (strcmp(cmdline
->commands
[i
].name
, cmd_name
) == 0) {
457 cmd
= &cmdline
->commands
[i
];
463 cmdline_usage_full(cmdline
);
467 poptSetOtherOptionHelp(cmdline
->pc
, "<command> [<args>]");
468 poptPrintUsage(cmdline
->pc
, stdout
, 0);
471 cmdline_usage_command(cmdline
, cmd
, false);
474 int cmdline_run(struct cmdline_context
*cmdline
,
478 struct cmdline_command
*cmd
= cmdline
->match_cmd
;
482 if (cmdline_show_help
) {
483 const char *name
= NULL
;
486 name
= cmdline
->match_cmd
->name
;
489 cmdline_usage(cmdline
, name
);
491 if (result
!= NULL
) {
501 tmp_ctx
= talloc_new(cmdline
);
502 if (tmp_ctx
== NULL
) {
506 ret
= cmd
->fn(tmp_ctx
,
507 cmdline
->argc
- cmdline
->arg0
,
508 &cmdline
->argv
[cmdline
->arg0
],
511 talloc_free(tmp_ctx
);
513 if (result
!= NULL
) {