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_section
{
34 struct cmdline_command
*commands
;
37 struct cmdline_context
{
39 struct poptOption
*options
;
40 struct cmdline_section
*section
;
46 struct cmdline_command
*match_cmd
;
49 static bool cmdline_show_help
= false;
51 static void cmdline_popt_help(poptContext pc
,
52 enum poptCallbackReason reason
,
53 struct poptOption
*key
,
57 if (key
->shortName
== 'h') {
58 cmdline_show_help
= true;
62 struct poptOption cmdline_help_options
[] = {
63 { NULL
, '\0', POPT_ARG_CALLBACK
, cmdline_popt_help
, 0, NULL
, NULL
},
64 { "help", 'h', 0, NULL
, 'h', "Show this help message", NULL
},
68 #define CMDLINE_HELP_OPTIONS \
69 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, cmdline_help_options, \
70 0, "Help Options:", NULL }
72 static bool cmdline_option_check(struct poptOption
*option
)
74 if (option
->longName
== NULL
) {
75 D_ERR("Option has no long name\n");
79 if (option
->argInfo
!= POPT_ARG_STRING
&&
80 option
->argInfo
!= POPT_ARG_INT
&&
81 option
->argInfo
!= POPT_ARG_LONG
&&
82 option
->argInfo
!= POPT_ARG_VAL
&&
83 option
->argInfo
!= POPT_ARG_FLOAT
&&
84 option
->argInfo
!= POPT_ARG_DOUBLE
) {
85 D_ERR("Option '%s' has unsupported type\n", option
->longName
);
89 if (option
->arg
== NULL
) {
90 D_ERR("Option '%s' has invalid arg\n", option
->longName
);
94 if (option
->descrip
== NULL
) {
95 D_ERR("Option '%s' has no help msg\n", option
->longName
);
102 static bool cmdline_options_check(struct poptOption
*options
)
107 if (options
== NULL
) {
112 while (options
[i
].longName
!= NULL
|| options
[i
].shortName
!= '\0') {
113 ok
= cmdline_option_check(&options
[i
]);
123 static int cmdline_options_define(TALLOC_CTX
*mem_ctx
,
124 struct poptOption
*user_options
,
125 struct poptOption
**result
)
127 struct poptOption
*options
;
130 count
= (user_options
== NULL
? 2 : 3);
132 options
= talloc_array(mem_ctx
, struct poptOption
, count
);
133 if (options
== NULL
) {
138 options
[i
++] = (struct poptOption
) CMDLINE_HELP_OPTIONS
;
139 if (user_options
!= NULL
) {
140 options
[i
++] = (struct poptOption
) {
141 .argInfo
= POPT_ARG_INCLUDE_TABLE
,
143 .descrip
= "Options:",
146 options
[i
++] = (struct poptOption
) POPT_TABLEEND
;
152 static bool cmdline_command_check(struct cmdline_command
*cmd
, size_t *max_len
)
156 if (cmd
->name
== NULL
) {
160 if (cmd
->fn
== NULL
) {
161 D_ERR("Command '%s' has no implementation function\n",
166 if (cmd
->msg_help
== NULL
) {
167 D_ERR("Command '%s' has no help msg\n", cmd
->name
);
171 len
= strlen(cmd
->name
);
172 if (cmd
->msg_args
!= NULL
) {
173 len
+= strlen(cmd
->msg_args
);
175 if (len
> CMDLINE_MAX_LEN
) {
176 D_ERR("Command '%s' is too long (%zu)\n", cmd
->name
, len
);
180 if (len
> *max_len
) {
184 len
= strlen(cmd
->msg_help
);
185 if (len
> CMDLINE_MAX_LEN
) {
186 D_ERR("Command '%s' help too long (%zu)\n", cmd
->name
, len
);
193 static bool cmdline_commands_check(struct cmdline_command
*commands
,
199 if (commands
== NULL
) {
203 for (i
=0; commands
[i
].name
!= NULL
; i
++) {
204 ok
= cmdline_command_check(&commands
[i
], max_len
);
213 static int cmdline_context_destructor(struct cmdline_context
*cmdline
);
215 static int cmdline_section_add(struct cmdline_context
*cmdline
,
217 struct cmdline_command
*commands
)
219 struct cmdline_section
*section
;
223 ok
= cmdline_commands_check(commands
, &max_len
);
228 section
= talloc_realloc(cmdline
,
230 struct cmdline_section
,
231 cmdline
->num_sections
+ 1);
232 if (section
== NULL
) {
236 section
[cmdline
->num_sections
] = (struct cmdline_section
) {
238 .commands
= commands
,
241 if (max_len
> cmdline
->max_len
) {
242 cmdline
->max_len
= max_len
;
245 cmdline
->section
= section
;
246 cmdline
->num_sections
+= 1;
251 int cmdline_init(TALLOC_CTX
*mem_ctx
,
253 struct poptOption
*options
,
255 struct cmdline_command
*commands
,
256 struct cmdline_context
**result
)
258 struct cmdline_context
*cmdline
;
266 ok
= cmdline_options_check(options
);
271 cmdline
= talloc_zero(mem_ctx
, struct cmdline_context
);
272 if (cmdline
== NULL
) {
276 cmdline
->prog
= talloc_strdup(cmdline
, prog
);
277 if (cmdline
->prog
== NULL
) {
278 talloc_free(cmdline
);
282 ret
= cmdline_options_define(cmdline
, options
, &cmdline
->options
);
284 talloc_free(cmdline
);
288 ret
= cmdline_section_add(cmdline
, name
, commands
);
290 talloc_free(cmdline
);
295 cmdline
->argv
= talloc_array(cmdline
, const char *, 2);
296 if (cmdline
->argv
== NULL
) {
297 talloc_free(cmdline
);
300 cmdline
->argv
[0] = cmdline
->prog
;
301 cmdline
->argv
[1] = NULL
;
303 /* Dummy popt context for generating help */
304 cmdline
->pc
= poptGetContext(cmdline
->prog
,
309 if (cmdline
->pc
== NULL
) {
310 talloc_free(cmdline
);
314 talloc_set_destructor(cmdline
, cmdline_context_destructor
);
320 static int cmdline_context_destructor(struct cmdline_context
*cmdline
)
322 if (cmdline
->pc
!= NULL
) {
323 poptFreeContext(cmdline
->pc
);
329 int cmdline_add(struct cmdline_context
*cmdline
,
331 struct cmdline_command
*commands
)
333 return cmdline_section_add(cmdline
, name
, commands
);
336 static int cmdline_parse_options(struct cmdline_context
*cmdline
,
342 if (cmdline
->pc
!= NULL
) {
343 poptFreeContext(cmdline
->pc
);
346 cmdline
->pc
= poptGetContext(cmdline
->prog
,
351 if (cmdline
->pc
== NULL
) {
355 while ((opt
= poptGetNextOpt(cmdline
->pc
)) != -1) {
356 D_ERR("Invalid option %s: %s\n",
357 poptBadOption(cmdline
->pc
, 0),
362 /* Set up remaining arguments for commands */
364 cmdline
->argv
= poptGetArgs(cmdline
->pc
);
365 if (cmdline
->argv
!= NULL
) {
366 while (cmdline
->argv
[cmdline
->argc
] != NULL
) {
374 static int cmdline_match_section(struct cmdline_context
*cmdline
,
375 struct cmdline_section
*section
)
379 for (i
=0; section
->commands
[i
].name
!= NULL
; i
++) {
380 struct cmdline_command
*cmd
;
381 char name
[CMDLINE_MAX_LEN
+1];
387 cmd
= §ion
->commands
[i
];
388 len
= strlcpy(name
, cmd
->name
, sizeof(name
));
389 if (len
>= sizeof(name
)) {
390 D_ERR("Skipping long command '%s'\n", cmd
->name
);
395 while ((t
= strtok(str
, " ")) != NULL
) {
396 if (n
>= cmdline
->argc
) {
400 if (cmdline
->argv
[n
] == NULL
) {
404 if (strcmp(cmdline
->argv
[n
], t
) == 0) {
417 cmdline
->match_cmd
= cmd
;
422 cmdline
->match_cmd
= NULL
;
426 static int cmdline_match(struct cmdline_context
*cmdline
)
430 if (cmdline
->argc
== 0 || cmdline
->argv
== NULL
) {
431 cmdline
->match_cmd
= NULL
;
435 for (i
=0; i
<cmdline
->num_sections
; i
++) {
436 ret
= cmdline_match_section(cmdline
, &cmdline
->section
[i
]);
445 int cmdline_parse(struct cmdline_context
*cmdline
,
453 cmdline_usage(cmdline
, NULL
);
457 cmdline_show_help
= false;
460 ret
= cmdline_parse_options(cmdline
, argc
, argv
);
462 cmdline_usage(cmdline
, NULL
);
466 cmdline
->argc
= argc
;
467 cmdline
->argv
= argv
;
470 ret
= cmdline_match(cmdline
);
472 if (ret
!= 0 || cmdline_show_help
) {
473 const char *name
= NULL
;
475 if (cmdline
->match_cmd
!= NULL
) {
476 name
= cmdline
->match_cmd
->name
;
479 cmdline_usage(cmdline
, name
);
481 if (cmdline_show_help
) {
489 static void cmdline_usage_command(struct cmdline_context
*cmdline
,
490 struct cmdline_command
*cmd
,
495 len
= strlen(cmd
->name
);
497 printf(" %s ", cmd
->name
);
500 (int)(cmdline
->max_len
-len
),
501 cmd
->msg_args
== NULL
? "" : cmd
->msg_args
);
503 printf("%s", cmd
->msg_args
== NULL
? "" : cmd
->msg_args
);
505 printf(" %s\n", cmd
->msg_help
);
508 static void cmdline_usage_section(struct cmdline_context
*cmdline
,
509 struct cmdline_section
*section
)
515 if (section
->name
!= NULL
) {
516 printf("%s ", section
->name
);
518 printf("Commands:\n");
519 for (i
=0; section
->commands
[i
].name
!= NULL
; i
++) {
520 cmdline_usage_command(cmdline
, §ion
->commands
[i
], true);
525 static void cmdline_usage_full(struct cmdline_context
*cmdline
)
529 poptSetOtherOptionHelp(cmdline
->pc
, "[<options>] <command> [<args>]");
530 poptPrintHelp(cmdline
->pc
, stdout
, 0);
532 for (i
=0; i
<cmdline
->num_sections
; i
++) {
533 cmdline_usage_section(cmdline
, &cmdline
->section
[i
]);
537 void cmdline_usage(struct cmdline_context
*cmdline
, const char *cmd_name
)
539 struct cmdline_command
*cmd
= NULL
;
542 if (cmd_name
== NULL
) {
543 cmdline_usage_full(cmdline
);
547 for (j
=0; j
<cmdline
->num_sections
; j
++) {
548 struct cmdline_section
*section
= &cmdline
->section
[j
];
550 for (i
=0; section
->commands
[i
].name
!= NULL
; i
++) {
551 if (strcmp(section
->commands
[i
].name
, cmd_name
) == 0) {
552 cmd
= §ion
->commands
[i
];
559 cmdline_usage_full(cmdline
);
563 poptSetOtherOptionHelp(cmdline
->pc
, "<command> [<args>]");
564 poptPrintUsage(cmdline
->pc
, stdout
, 0);
567 cmdline_usage_command(cmdline
, cmd
, false);
570 int cmdline_run(struct cmdline_context
*cmdline
,
574 struct cmdline_command
*cmd
= cmdline
->match_cmd
;
582 tmp_ctx
= talloc_new(cmdline
);
583 if (tmp_ctx
== NULL
) {
587 ret
= cmd
->fn(tmp_ctx
,
588 cmdline
->argc
- cmdline
->arg0
,
589 &cmdline
->argv
[cmdline
->arg0
],
592 talloc_free(tmp_ctx
);
594 if (result
!= NULL
) {