s3:include: Remove trailing whitepaces in MacExtensions.h
[Samba.git] / ctdb / common / cmdline.c
blobce368a9b2417ed149568d8e1a8254a12bcc042ff
1 /*
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/>.
20 #include "replace.h"
22 #include <popt.h>
23 #include <talloc.h>
24 #include <tevent.h>
26 #include "lib/util/debug.h"
28 #include "common/cmdline.h"
30 #define CMDLINE_MAX_LEN 80
32 struct cmdline_section {
33 const char *name;
34 struct cmdline_command *commands;
37 struct cmdline_context {
38 const char *prog;
39 struct poptOption *options;
40 struct cmdline_section *section;
41 int num_sections;
42 size_t max_len;
43 poptContext pc;
44 int argc, arg0;
45 const char **argv;
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,
54 const char *arg,
55 void *data)
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 },
65 POPT_TABLEEND
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");
76 return false;
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);
86 return false;
89 if (option->arg == NULL) {
90 D_ERR("Option '%s' has invalid arg\n", option->longName);
91 return false;
94 if (option->descrip == NULL) {
95 D_ERR("Option '%s' has no help msg\n", option->longName);
96 return false;
99 return true;
102 static bool cmdline_options_check(struct poptOption *options)
104 int i;
105 bool ok;
107 if (options == NULL) {
108 return true;
111 i = 0;
112 while (options[i].longName != NULL || options[i].shortName != '\0') {
113 ok = cmdline_option_check(&options[i]);
114 if (!ok) {
115 return false;
117 i++;
120 return true;
123 static int cmdline_options_define(TALLOC_CTX *mem_ctx,
124 struct poptOption *user_options,
125 struct poptOption **result)
127 struct poptOption *options;
128 int count, i;
130 count = (user_options == NULL ? 2 : 3);
132 options = talloc_array(mem_ctx, struct poptOption, count);
133 if (options == NULL) {
134 return ENOMEM;
137 i = 0;
138 options[i++] = (struct poptOption) CMDLINE_HELP_OPTIONS;
139 if (user_options != NULL) {
140 options[i++] = (struct poptOption) {
141 .argInfo = POPT_ARG_INCLUDE_TABLE,
142 .arg = user_options,
143 .descrip = "Options:",
146 options[i++] = (struct poptOption) POPT_TABLEEND;
148 *result = options;
149 return 0;
152 static bool cmdline_command_check(struct cmdline_command *cmd, size_t *max_len)
154 size_t len;
156 if (cmd->name == NULL) {
157 return false;
160 if (cmd->fn == NULL) {
161 D_ERR("Command '%s' has no implementation function\n",
162 cmd->name);
163 return false;
166 if (cmd->msg_help == NULL) {
167 D_ERR("Command '%s' has no help msg\n", cmd->name);
168 return false;
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);
177 return false;
180 if (len > *max_len) {
181 *max_len = 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);
187 return false;
190 return true;
193 static bool cmdline_commands_check(struct cmdline_command *commands,
194 size_t *max_len)
196 int i;
197 bool ok;
199 if (commands == NULL) {
200 return false;
203 for (i=0; commands[i].name != NULL; i++) {
204 ok = cmdline_command_check(&commands[i], max_len);
205 if (!ok) {
206 return false;
210 return true;
213 static int cmdline_context_destructor(struct cmdline_context *cmdline);
215 static int cmdline_section_add(struct cmdline_context *cmdline,
216 const char *name,
217 struct cmdline_command *commands)
219 struct cmdline_section *section;
220 size_t max_len = 0;
221 bool ok;
223 ok = cmdline_commands_check(commands, &max_len);
224 if (!ok) {
225 return EINVAL;
228 section = talloc_realloc(cmdline,
229 cmdline->section,
230 struct cmdline_section,
231 cmdline->num_sections + 1);
232 if (section == NULL) {
233 return ENOMEM;
236 section[cmdline->num_sections] = (struct cmdline_section) {
237 .name = name,
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;
248 return 0;
251 int cmdline_init(TALLOC_CTX *mem_ctx,
252 const char *prog,
253 struct poptOption *options,
254 const char *name,
255 struct cmdline_command *commands,
256 struct cmdline_context **result)
258 struct cmdline_context *cmdline;
259 int ret;
260 bool ok;
262 if (prog == NULL) {
263 return EINVAL;
266 ok = cmdline_options_check(options);
267 if (!ok) {
268 return EINVAL;
271 cmdline = talloc_zero(mem_ctx, struct cmdline_context);
272 if (cmdline == NULL) {
273 return ENOMEM;
276 cmdline->prog = talloc_strdup(cmdline, prog);
277 if (cmdline->prog == NULL) {
278 talloc_free(cmdline);
279 return ENOMEM;
282 ret = cmdline_options_define(cmdline, options, &cmdline->options);
283 if (ret != 0) {
284 talloc_free(cmdline);
285 return ret;
288 ret = cmdline_section_add(cmdline, name, commands);
289 if (ret != 0) {
290 talloc_free(cmdline);
291 return ret;
294 cmdline->argc = 1;
295 cmdline->argv = talloc_array(cmdline, const char *, 2);
296 if (cmdline->argv == NULL) {
297 talloc_free(cmdline);
298 return ENOMEM;
300 cmdline->argv[0] = cmdline->prog;
301 cmdline->argv[1] = NULL;
303 /* Dummy popt context for generating help */
304 cmdline->pc = poptGetContext(cmdline->prog,
305 cmdline->argc,
306 cmdline->argv,
307 cmdline->options,
309 if (cmdline->pc == NULL) {
310 talloc_free(cmdline);
311 return ENOMEM;
314 talloc_set_destructor(cmdline, cmdline_context_destructor);
316 *result = cmdline;
317 return 0;
320 static int cmdline_context_destructor(struct cmdline_context *cmdline)
322 if (cmdline->pc != NULL) {
323 poptFreeContext(cmdline->pc);
326 return 0;
329 int cmdline_add(struct cmdline_context *cmdline,
330 const char *name,
331 struct cmdline_command *commands)
333 return cmdline_section_add(cmdline, name, commands);
336 static int cmdline_parse_options(struct cmdline_context *cmdline,
337 int argc,
338 const char **argv)
340 int opt;
342 if (cmdline->pc != NULL) {
343 poptFreeContext(cmdline->pc);
346 cmdline->pc = poptGetContext(cmdline->prog,
347 argc,
348 argv,
349 cmdline->options,
351 if (cmdline->pc == NULL) {
352 return ENOMEM;
355 while ((opt = poptGetNextOpt(cmdline->pc)) != -1) {
356 D_ERR("Invalid option %s: %s\n",
357 poptBadOption(cmdline->pc, 0),
358 poptStrerror(opt));
359 return EINVAL;
362 /* Set up remaining arguments for commands */
363 cmdline->argc = 0;
364 cmdline->argv = poptGetArgs(cmdline->pc);
365 if (cmdline->argv != NULL) {
366 while (cmdline->argv[cmdline->argc] != NULL) {
367 cmdline->argc++;
371 return 0;
374 static int cmdline_match_section(struct cmdline_context *cmdline,
375 struct cmdline_section *section)
377 int i;
379 for (i=0; section->commands[i].name != NULL; i++) {
380 struct cmdline_command *cmd;
381 char name[CMDLINE_MAX_LEN+1];
382 size_t len;
383 char *t, *str;
384 int n = 0;
385 bool match = false;
387 cmd = &section->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);
391 continue;
394 str = name;
395 while ((t = strtok(str, " ")) != NULL) {
396 if (n >= cmdline->argc) {
397 match = false;
398 break;
400 if (cmdline->argv[n] == NULL) {
401 match = false;
402 break;
404 if (strcmp(cmdline->argv[n], t) == 0) {
405 match = true;
406 cmdline->arg0 = n+1;
407 } else {
408 match = false;
409 break;
412 n += 1;
413 str = NULL;
416 if (match) {
417 cmdline->match_cmd = cmd;
418 return 0;
422 cmdline->match_cmd = NULL;
423 return ENOENT;
426 static int cmdline_match(struct cmdline_context *cmdline)
428 int i, ret = ENOENT;
430 if (cmdline->argc == 0 || cmdline->argv == NULL) {
431 cmdline->match_cmd = NULL;
432 return EINVAL;
435 for (i=0; i<cmdline->num_sections; i++) {
436 ret = cmdline_match_section(cmdline, &cmdline->section[i]);
437 if (ret == 0) {
438 break;
442 return ret;
445 int cmdline_parse(struct cmdline_context *cmdline,
446 int argc,
447 const char **argv,
448 bool parse_options)
450 int ret;
452 if (argc < 2) {
453 cmdline_usage(cmdline, NULL);
454 return EINVAL;
457 cmdline_show_help = false;
459 if (parse_options) {
460 ret = cmdline_parse_options(cmdline, argc, argv);
461 if (ret != 0) {
462 cmdline_usage(cmdline, NULL);
463 return ret;
465 } else {
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) {
482 ret = EAGAIN;
486 return ret;
489 static void cmdline_usage_command(struct cmdline_context *cmdline,
490 struct cmdline_command *cmd,
491 bool print_all)
493 size_t len;
495 len = strlen(cmd->name);
497 printf(" %s ", cmd->name);
498 if (print_all) {
499 printf("%-*s",
500 (int)(cmdline->max_len-len),
501 cmd->msg_args == NULL ? "" : cmd->msg_args);
502 } else {
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)
511 int i;
513 printf("\n");
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, &section->commands[i], true);
525 static void cmdline_usage_full(struct cmdline_context *cmdline)
527 int i;
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;
540 int i, j;
542 if (cmd_name == NULL) {
543 cmdline_usage_full(cmdline);
544 return;
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 = &section->commands[i];
553 break;
558 if (cmd == NULL) {
559 cmdline_usage_full(cmdline);
560 return;
563 poptSetOtherOptionHelp(cmdline->pc, "<command> [<args>]");
564 poptPrintUsage(cmdline->pc, stdout, 0);
566 printf("\n");
567 cmdline_usage_command(cmdline, cmd, false);
570 int cmdline_run(struct cmdline_context *cmdline,
571 void *private_data,
572 int *result)
574 struct cmdline_command *cmd = cmdline->match_cmd;
575 TALLOC_CTX *tmp_ctx;
576 int ret;
578 if (cmd == NULL) {
579 return ENOENT;
582 tmp_ctx = talloc_new(cmdline);
583 if (tmp_ctx == NULL) {
584 return ENOMEM;
587 ret = cmd->fn(tmp_ctx,
588 cmdline->argc - cmdline->arg0,
589 &cmdline->argv[cmdline->arg0],
590 private_data);
592 talloc_free(tmp_ctx);
594 if (result != NULL) {
595 *result = ret;
597 return 0;