Line data Source code
1 : /*********************************** LICENSE **********************************\
2 : * Copyright 2017 Morphux *
3 : * *
4 : * Licensed under the Apache License, Version 2.0 (the "License"); *
5 : * you may not use this file except in compliance with the License. *
6 : * You may obtain a copy of the License at *
7 : * *
8 : * http://www.apache.org/licenses/LICENSE-2.0 *
9 : * *
10 : * Unless required by applicable law or agreed to in writing, software *
11 : * distributed under the License is distributed on an "AS IS" BASIS, *
12 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
13 : * See the License for the specific language governing permissions and *
14 : * limitations under the License. *
15 : \******************************************************************************/
16 :
17 : #include <m_args.h>
18 : #define LIB_OPT_TOKEN_HELP 'h'
19 : #define LIB_OPT_STRING_HELP "help"
20 : #define LIB_OPT_TOKEN_VERSION 'V'
21 : #define LIB_OPT_STRING_VERSION "version"
22 :
23 : /*!
24 : * \brief Read the options given by the program
25 : * \param ac Number of argument in av
26 : * \param av Array of string, containing the arguments
27 : * \param args Array of margs_t, containing the preset options. Must end with
28 : * an empty structure.
29 : * \return Number of options read
30 : *
31 : * The read_opt function reads a given list of arguments, and parse the options
32 : * in it. The options are read from the args array.
33 : * If an option is not known, the function calls the help and quit.
34 : * If the option -h | --help is passed, the function call the help and quit.
35 : * If the option -v | --version is passed, the function call the version and quit.
36 : *
37 : * \note Only the arguments beginning with - are parsed.
38 : */
39 32 : u32_t read_opt(const int ac, char **av, const margs_t *args) {
40 32 : u32_t ret = 0, it, k;
41 : u8_t n_dash;
42 :
43 32 : if (ac == 0 || av == NULL || args == NULL)
44 6 : return ret;
45 :
46 59 : for (u32_t i = 1, j = 0; i < (u32_t)ac; i++) {
47 :
48 41 : if (av[i] == NULL || strlen(av[i]) == 0)
49 4 : continue ;
50 :
51 37 : n_dash = 0;
52 92 : for (j = 0; av[i][j] != '\0' && av[i][j] == '-'; j++)
53 55 : n_dash++;
54 :
55 : /* Argument have more than two '-' */
56 37 : if (n_dash > 2) {
57 1 : m_error("Malformed option: %s\n", av[i]);
58 1 : continue ;
59 : }
60 :
61 : /* Single letter option */
62 36 : if (n_dash == 1) {
63 :
64 14 : if (strlen(av[i]) < 2) {
65 2 : m_error("Dash without option. Ignoring...\n");
66 2 : continue ;
67 : }
68 :
69 : /* Builtins options */
70 12 : if (av[i][1] == LIB_OPT_TOKEN_HELP)
71 1 : opt_help(args, 0);
72 11 : else if (av[i][1] == LIB_OPT_TOKEN_VERSION)
73 1 : p_version(0);
74 :
75 : /* Search the option in the args array */
76 22 : for (u32_t z = 1; av[i][z] != '\0'; z++) {
77 16 : for (it = 0; !IS_EOL(args[it]) && args[it].opt != av[i][z]; it++)
78 : ;
79 :
80 : /* Can't find the option */
81 16 : if (IS_EOL(args[it])) {
82 1 : m_error("Unknow option -%s\n", &(av[i][z]));
83 1 : opt_help(args, 1);
84 : } else {
85 15 : if (args[it].take_arg) {
86 3 : if (i + 1 < (u32_t)ac) {
87 2 : args[it].callback(av[++i]);
88 2 : ret++;
89 2 : break ;
90 : } else {
91 1 : m_error("Option -%c must take an argument\n",
92 1 : args[it].opt);
93 1 : opt_help(args, 1);
94 : }
95 : } else {
96 12 : args[it].callback(NULL);
97 12 : ret++;
98 : }
99 : }
100 : }
101 :
102 : /* Word option */
103 22 : } else if (n_dash == 2) {
104 19 : bool got_arg = false;
105 :
106 19 : if (strlen(av[i]) < 3) {
107 1 : m_error("Double dash without option. Ignoring...\n");
108 1 : continue ;
109 : }
110 :
111 : /* Builtins options */
112 18 : if (strcmp(&(av[i][2]), LIB_OPT_STRING_HELP) == 0)
113 1 : opt_help(args, 0);
114 17 : else if (strcmp(&(av[i][2]), LIB_OPT_STRING_VERSION) == 0)
115 1 : p_version(0);
116 :
117 : /* Look for an argument */
118 16 : for (k = 2; av[i][k] != '\0' && av[i][k] != '='; k++)
119 : ;
120 :
121 16 : if (av[i][k] != '\0') {
122 4 : got_arg = true;
123 4 : k -= 2;
124 : } else {
125 12 : k = strlen(av[i]) - 2;
126 : }
127 :
128 : /* Search the option in the args array */
129 83 : for (it = 0; !IS_EOL(args[it]) &&
130 51 : (strncmp(args[it].s_opt, &(av[i][2]), k) != 0); it++)
131 : ;
132 :
133 : /* Can't find the option */
134 16 : if (IS_EOL(args[it])) {
135 1 : m_error("Unknown option %s\n", av[i]);
136 1 : opt_help(args, 1);
137 : } else {
138 15 : if (args[it].take_arg && !got_arg) {
139 1 : m_error("Option %s must take an argument", args[it].s_opt);
140 1 : opt_help(args, 1);
141 : }
142 14 : if (got_arg)
143 4 : args[it].callback(&(av[i][k + 3]));
144 : else
145 10 : args[it].callback(NULL);
146 14 : ret++;
147 : }
148 : }
149 : }
150 18 : return ret;
151 : }
152 :
153 : /*!
154 : * \brief Print helps with a list of argument, and exit
155 : * \param args List of arguments to print
156 : * \param ret Return code of the exit
157 : */
158 6 : void opt_help(const margs_t *args, u8_t ret) {
159 6 : m_info("Help:\n");
160 22 : for (u32_t i = 0; args[i].opt != 0; i++) {
161 16 : m_info("\t-%c | --%s : %s\n", args[i].opt, args[i].s_opt, args[i].desc);
162 : }
163 6 : write(1, "\n", 1);
164 6 : m_info("If an argument requires a value, you can set it two ways:\n");
165 6 : m_info("\t-o value\n");
166 6 : m_info("\t--option=value\n");
167 6 : exit(ret);
168 : }
169 :
170 : /*!
171 : * \brief Print the program name, the version and the maintainer, then exit
172 : * \param ret Return code of the exit
173 : */
174 2 : void p_version(u8_t ret) {
175 2 : m_info("Program: %s\n", get_program_name());
176 2 : m_info("Version: %s\n", get_version());
177 2 : m_info("%s\n", get_maintainer());
178 2 : exit(ret);
179 : }
|