1/*
2 * cmdl.c	Framework for handling command line options.
3 *
4 *		This program is free software; you can redistribute it and/or
5 *		modify it under the terms of the GNU General Public License
6 *		as published by the Free Software Foundation; either version
7 *		2 of the License, or (at your option) any later version.
8 *
9 * Authors:	Richard Alpe <richard.alpe@ericsson.com>
10 */
11
12#include <stdio.h>
13#include <string.h>
14#include <errno.h>
15
16#include <libmnl/libmnl.h>
17
18#include "cmdl.h"
19
20const struct cmd *find_cmd(const struct cmd *cmds, char *str)
21{
22	const struct cmd *c;
23	const struct cmd *match = NULL;
24
25	for (c = cmds; c->cmd; c++) {
26		if (strstr(c->cmd, str) != c->cmd)
27			continue;
28		if (match)
29			return NULL;
30		match = c;
31	}
32
33	return match;
34}
35
36static struct opt *find_opt(struct opt *opts, char *str)
37{
38	struct opt *o;
39	struct opt *match = NULL;
40
41	for (o = opts; o->key; o++) {
42		if (strstr(o->key, str) != o->key)
43			continue;
44		if (match)
45			return NULL;
46
47		match = o;
48	}
49
50	return match;
51}
52
53struct opt *get_opt(struct opt *opts, char *key)
54{
55	struct opt *o;
56
57	for (o = opts; o->key; o++) {
58		if (strcmp(o->key, key) == 0 && o->val)
59			return o;
60	}
61
62	return NULL;
63}
64
65char *shift_cmdl(struct cmdl *cmdl)
66{
67	int next;
68
69	if (cmdl->optind < cmdl->argc)
70		next = (cmdl->optind)++;
71	else
72		next = cmdl->argc;
73
74	return cmdl->argv[next];
75}
76
77/* Returns the number of options parsed or a negative error code upon failure */
78int parse_opts(struct opt *opts, struct cmdl *cmdl)
79{
80	int i;
81	int cnt = 0;
82
83	for (i = cmdl->optind; i < cmdl->argc; i += 2) {
84		struct opt *o;
85
86		o = find_opt(opts, cmdl->argv[i]);
87		if (!o) {
88			fprintf(stderr, "error, invalid option \"%s\"\n",
89					cmdl->argv[i]);
90			return -EINVAL;
91		}
92		cnt++;
93		o->val = cmdl->argv[i + 1];
94		cmdl->optind += 2;
95	}
96
97	return cnt;
98}
99
100int run_cmd(struct nlmsghdr *nlh, const struct cmd *caller,
101	    const struct cmd *cmds, struct cmdl *cmdl, void *data)
102{
103	char *name;
104	const struct cmd *cmd;
105
106	if ((cmdl->optind) >= cmdl->argc) {
107		if (caller->help)
108			(caller->help)(cmdl);
109		return -EINVAL;
110	}
111	name = cmdl->argv[cmdl->optind];
112	(cmdl->optind)++;
113
114	cmd = find_cmd(cmds, name);
115	if (!cmd) {
116		/* Show help about last command if we don't find this one */
117		if (help_flag && caller->help) {
118			(caller->help)(cmdl);
119		} else {
120			fprintf(stderr, "error, invalid command \"%s\"\n", name);
121			fprintf(stderr, "use --help for command help\n");
122		}
123		return -EINVAL;
124	}
125
126	return (cmd->func)(nlh, cmd, cmdl, data);
127}
128