wscript: separate embedded_heimdal from system_heimdal
[Samba.git] / ctdb / common / cmdline.c
blobee360d4c9208231635028b21fbb3f8043706483b
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_context {
33 const char *prog;
34 struct poptOption *options;
35 struct cmdline_command *commands;
36 int max_len;
37 poptContext pc;
38 int argc, arg0;
39 const char **argv;
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,
48 const char *arg,
49 void *data)
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 },
59 POPT_TABLEEND
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");
70 return false;
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);
80 return false;
83 if (option->arg == NULL) {
84 D_ERR("Option '%s' has invalid arg\n", option->longName);
85 return false;
88 if (option->descrip == NULL) {
89 D_ERR("Option '%s' has no help msg\n", option->longName);
90 return false;
93 return true;
96 static bool cmdline_options_check(struct poptOption *options)
98 int i;
99 bool ok;
101 if (options == NULL) {
102 return true;
105 i = 0;
106 while (options[i].longName != NULL || options[i].shortName != '\0') {
107 ok = cmdline_option_check(&options[i]);
108 if (!ok) {
109 return false;
111 i++;
114 return true;
117 static int cmdline_options_define(TALLOC_CTX *mem_ctx,
118 struct poptOption *user_options,
119 struct poptOption **result)
121 struct poptOption *options;
122 int count, i;
124 count = (user_options == NULL ? 2 : 3);
126 options = talloc_array(mem_ctx, struct poptOption, count);
127 if (options == NULL) {
128 return ENOMEM;
131 i = 0;
132 options[i++] = (struct poptOption) CMDLINE_HELP_OPTIONS;
133 if (user_options != NULL) {
134 options[i++] = (struct poptOption) {
135 .argInfo = POPT_ARG_INCLUDE_TABLE,
136 .arg = user_options,
137 .descrip = "Options:",
140 options[i++] = (struct poptOption) POPT_TABLEEND;
142 *result = options;
143 return 0;
146 static bool cmdline_command_check(struct cmdline_command *cmd, int *max_len)
148 size_t len;
150 if (cmd->name == NULL) {
151 return false;
154 if (cmd->fn == NULL) {
155 D_ERR("Command '%s' has no implementation function\n",
156 cmd->name);
157 return false;
160 if (cmd->msg_help == NULL) {
161 D_ERR("Command '%s' has no help msg\n", cmd->name);
162 return false;
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);
171 return false;
174 if (len > *max_len) {
175 *max_len = (int)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);
181 return false;
184 return true;
187 static bool cmdline_commands_check(struct cmdline_command *commands,
188 int *max_len)
190 int i;
191 bool ok;
193 if (commands == NULL) {
194 return false;
197 for (i=0; commands[i].name != NULL; i++) {
198 ok = cmdline_command_check(&commands[i], max_len);
199 if (!ok) {
200 return false;
204 return true;
207 static int cmdline_context_destructor(struct cmdline_context *cmdline);
209 int cmdline_init(TALLOC_CTX *mem_ctx,
210 const char *prog,
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;
217 bool ok;
219 if (prog == NULL) {
220 return EINVAL;
223 ok = cmdline_options_check(options);
224 if (!ok) {
225 return EINVAL;
228 ok = cmdline_commands_check(commands, &max_len);
229 if (!ok) {
230 return EINVAL;
233 cmdline = talloc_zero(mem_ctx, struct cmdline_context);
234 if (cmdline == NULL) {
235 return ENOMEM;
238 cmdline->prog = talloc_strdup(cmdline, prog);
239 if (cmdline->prog == NULL) {
240 talloc_free(cmdline);
241 return ENOMEM;
244 ret = cmdline_options_define(cmdline, options, &cmdline->options);
245 if (ret != 0) {
246 talloc_free(cmdline);
247 return ret;
249 cmdline->commands = commands;
250 cmdline->max_len = max_len;
252 cmdline->argc = 1;
253 cmdline->argv = talloc_array(cmdline, const char *, 2);
254 if (cmdline->argv == NULL) {
255 talloc_free(cmdline);
256 return ENOMEM;
258 cmdline->argv[0] = cmdline->prog;
259 cmdline->argv[1] = NULL;
261 /* Dummy popt context for generating help */
262 cmdline->pc = poptGetContext(cmdline->prog,
263 cmdline->argc,
264 cmdline->argv,
265 cmdline->options,
267 if (cmdline->pc == NULL) {
268 talloc_free(cmdline);
269 return ENOMEM;
272 talloc_set_destructor(cmdline, cmdline_context_destructor);
274 *result = cmdline;
275 return 0;
278 static int cmdline_context_destructor(struct cmdline_context *cmdline)
280 if (cmdline->pc != NULL) {
281 poptFreeContext(cmdline->pc);
284 return 0;
287 static int cmdline_parse_options(struct cmdline_context *cmdline,
288 int argc,
289 const char **argv)
291 int opt;
293 if (cmdline->pc != NULL) {
294 poptFreeContext(cmdline->pc);
297 cmdline->pc = poptGetContext(cmdline->prog,
298 argc,
299 argv,
300 cmdline->options,
302 if (cmdline->pc == NULL) {
303 return ENOMEM;
306 while ((opt = poptGetNextOpt(cmdline->pc)) != -1) {
307 D_ERR("Invalid option %s: %s\n",
308 poptBadOption(cmdline->pc, 0),
309 poptStrerror(opt));
310 return EINVAL;
313 /* Set up remaining arguments for commands */
314 cmdline->argc = 0;
315 cmdline->argv = poptGetArgs(cmdline->pc);
316 if (cmdline->argv != NULL) {
317 while (cmdline->argv[cmdline->argc] != NULL) {
318 cmdline->argc++;
322 return 0;
325 static int cmdline_match(struct cmdline_context *cmdline)
327 int i;
329 if (cmdline->argc == 0 || cmdline->argv == NULL) {
330 cmdline->match_cmd = NULL;
331 return EINVAL;
334 for (i=0; cmdline->commands[i].name != NULL; i++) {
335 struct cmdline_command *cmd;
336 char name[CMDLINE_MAX_LEN+1];
337 size_t len;
338 char *t, *str;
339 int n = 0;
340 bool match = false;
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);
346 continue;
349 str = name;
350 while ((t = strtok(str, " ")) != NULL) {
351 if (n >= cmdline->argc) {
352 match = false;
353 break;
355 if (cmdline->argv[n] == NULL) {
356 match = false;
357 break;
359 if (strcmp(cmdline->argv[n], t) == 0) {
360 match = true;
361 cmdline->arg0 = n+1;
362 } else {
363 match = false;
364 break;
367 n += 1;
368 str = NULL;
371 if (match) {
372 cmdline->match_cmd = cmd;
373 return 0;
377 cmdline->match_cmd = NULL;
378 return ENOENT;
381 int cmdline_parse(struct cmdline_context *cmdline,
382 int argc,
383 const char **argv,
384 bool parse_options)
386 int ret;
388 if (argc < 2) {
389 return EINVAL;
392 cmdline_show_help = false;
394 if (parse_options) {
395 ret = cmdline_parse_options(cmdline, argc, argv);
396 if (ret != 0) {
397 return ret;
399 } else {
400 cmdline->argc = argc;
401 cmdline->argv = argv;
404 ret = cmdline_match(cmdline);
405 if (!cmdline_show_help && ret != 0) {
406 return ret;
409 return 0;
412 static void cmdline_usage_command(struct cmdline_context *cmdline,
413 struct cmdline_command *cmd,
414 bool print_all)
416 int len;
418 len = (int)strlen(cmd->name);
420 printf(" %s ", cmd->name);
421 if (print_all) {
422 printf("%-*s",
423 cmdline->max_len-len,
424 cmd->msg_args == NULL ? "" : cmd->msg_args);
425 } else {
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)
433 int i;
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;
448 int i;
450 if (cmd_name == NULL) {
451 cmdline_usage_full(cmdline);
452 return;
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];
458 break;
462 if (cmd == NULL) {
463 cmdline_usage_full(cmdline);
464 return;
467 poptSetOtherOptionHelp(cmdline->pc, "<command> [<args>]");
468 poptPrintUsage(cmdline->pc, stdout, 0);
470 printf("\n");
471 cmdline_usage_command(cmdline, cmd, false);
474 int cmdline_run(struct cmdline_context *cmdline,
475 void *private_data,
476 int *result)
478 struct cmdline_command *cmd = cmdline->match_cmd;
479 TALLOC_CTX *tmp_ctx;
480 int ret;
482 if (cmdline_show_help) {
483 const char *name = NULL;
485 if (cmd != NULL) {
486 name = cmdline->match_cmd->name;
489 cmdline_usage(cmdline, name);
491 if (result != NULL) {
492 *result = 0;
494 return EAGAIN;
497 if (cmd == NULL) {
498 return ENOENT;
501 tmp_ctx = talloc_new(cmdline);
502 if (tmp_ctx == NULL) {
503 return ENOMEM;
506 ret = cmd->fn(tmp_ctx,
507 cmdline->argc - cmdline->arg0,
508 &cmdline->argv[cmdline->arg0],
509 private_data);
511 talloc_free(tmp_ctx);
513 if (result != NULL) {
514 *result = ret;
516 return 0;