Remove some wrong assert's.
[vsc-common.git] / cli / option.c
1 /*
2  * option.c: Library for interactive commandline interfaces
3  *
4  * Copyright (C) 2009 Matthias Bolte <matthias.bolte@googlemail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
19  */
20
21 #include <string.h>
22
23 #include <libvscmisc/assert.h>
24 #include <libvscmisc/error.h>
25 #include <libvscmisc/list.h>
26 #include <libvscmisc/memory.h>
27 #include <libvscmisc/string.h>
28
29 #include "../include/libvsccli/option.h"
30 #include "option.h"
31 #include "token.h"
32
33 const char *
34 _vsc_cli_option_syntax (const struct VscCliOptionInfo *option_info,
35                         int synopsis)
36 {
37         static char buffer1[256];
38         static char buffer2[256];
39         static char buffer3[512];
40
41         VSC__ASSERT (option_info != NULL);
42
43         switch (option_info->type) {
44         case VSC_CLI__OPTION_TYPE__DATA:
45                 snprintf (buffer3, 512, "<%s>", option_info->long_name);
46                 return buffer3;
47
48         case VSC_CLI__OPTION_TYPE__BOOL:
49                 snprintf (buffer1, 256, "-%c", option_info->short_name);
50                 snprintf (buffer2, 256, "--%s", option_info->long_name);
51                 break;
52
53         case VSC_CLI__OPTION_TYPE__NUMBER:
54                 snprintf (buffer1, 256, "-%c <number>", option_info->short_name);
55                 snprintf (buffer2, 256, "--%s <number>", option_info->long_name);
56                 break;
57
58         case VSC_CLI__OPTION_TYPE__STRING:
59                 snprintf (buffer1, 256, "-%c <string>", option_info->short_name);
60                 snprintf (buffer2, 256, "--%s <string>", option_info->long_name);
61                 break;
62
63         default:
64                 snprintf (buffer1, 256, "INVALID");
65                 snprintf (buffer2, 256, "INVALID");
66                 break;
67         }
68
69         if (synopsis && option_info->short_name != -1) {
70                 return buffer1;
71         }
72
73         if (option_info->short_name == -1) {
74                 return buffer2;
75         }
76
77         snprintf (buffer3, 512, "%s, %s", buffer1, buffer2);
78
79         return buffer3;
80 }
81
82 int /* num_options_parsed */
83 _vsc_cli_option_parse (struct VscError *error,
84                        const struct VscCliOptionInfo *option_infos,
85                        int data_option_index, const char *command_name,
86                        const char *input, const char **input_tail,
87                        struct VscCliOption *option)
88 {
89         int num_options_parsed = 1;
90         const struct VscCliOptionInfo *option_info;
91         const char *tail = NULL;
92         char *token = NULL;
93         int num_dashes = 0;
94
95         VSC__ASSERT (error != NULL);
96         VSC__ASSERT (! error->occured);
97         VSC__ASSERT (option_infos != NULL);
98         VSC__ASSERT (input != NULL);
99         VSC__ASSERT (input_tail != NULL);
100         VSC__ASSERT (option != NULL);
101
102         *input_tail = input;
103
104         _vsc_cli_token_parse (error, input, &token, &tail);
105
106         if (error->occured) {
107                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
108                 goto failure;
109         }
110
111         if (token == NULL) {
112                 return 0;
113         }
114
115         memset (option, 0, sizeof (struct VscCliOption));
116
117         if (*token == '-') {
118                 ++num_dashes;
119
120                 if (*(token + 1) == '-') {
121                         ++num_dashes;
122                 }
123         }
124
125         if (num_dashes > 0) {
126                 option_info = _vsc_cli_option_infos_lookup
127                                  (option_infos, token + num_dashes, num_dashes > 1);
128
129                 if (option_info == NULL) {
130                         if (command_name != NULL) {
131                                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
132                                              "unknown option %s for command '%s'",
133                                              token, command_name);
134                         } else  {
135                                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
136                                              "unknown option %s", token);
137                         }
138
139                         goto failure;
140                 }
141
142                 option->info = option_info;
143
144                 switch (option_info->type) {
145                 case VSC_CLI__OPTION_TYPE__BOOL:
146                         break;
147
148                 case VSC_CLI__OPTION_TYPE__NUMBER:
149                         vsc_free (&token);
150                         _vsc_cli_token_parse (error, tail, &token, &tail);
151
152                         if (error->occured) {
153                                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
154                                 goto failure;
155                         }
156
157                         if (token == NULL) {
158                                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
159                                              "expected option syntax: %s",
160                                              _vsc_cli_option_syntax (option_info, FALSE));
161                                 goto failure;
162                         }
163
164                         option->number = vsc_strtoi (error, token, NULL, 10);
165
166                         if (error->occured) {
167                                 vsc_error_cleanup (error);
168                                 vsc_error_init (error);
169
170                                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
171                                              "expected option syntax: %s",
172                                              _vsc_cli_option_syntax (option_info, FALSE));
173                                 goto failure;
174                         }
175
176                         break;
177
178                 case VSC_CLI__OPTION_TYPE__STRING:
179                         vsc_free (&token);
180                         _vsc_cli_token_parse (error, tail, &token, &tail);
181
182                         if (error->occured) {
183                                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
184                                 goto failure;
185                         }
186
187                         if (token == NULL) {
188                                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
189                                              "expected option syntax: %s",
190                                              _vsc_cli_option_syntax (option_info, FALSE));
191                                 goto failure;
192                         }
193
194                         option->string = token;
195                         token = NULL;
196                         break;
197
198                 default:
199                         VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
200                                      "internal error");
201                         goto failure;
202                 }
203
204                 if (error->occured) {
205                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
206                         goto failure;
207                 }
208         } else if (data_option_index >= 0) {
209                 option_info = _vsc_cli_option_infos_lookup_data
210                                  (option_infos, data_option_index);
211
212                 if (option_info == NULL) {
213                         VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
214                                      "too many options for command '%s'",
215                                      command_name);
216                         goto failure;
217                 }
218
219                 option->info = option_info;
220                 option->string = token;
221                 token = NULL;
222
223                 if (error->occured) {
224                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
225                         goto failure;
226                 }
227         } else {
228                 num_options_parsed = 0;
229         }
230
231         if (num_options_parsed > 0) {
232                 *input_tail = tail;
233         }
234
235 cleanup:
236         vsc_free (&token);
237
238         return num_options_parsed;
239
240 failure:
241         vsc_free (&option->string);
242
243         num_options_parsed = -1;
244
245         goto cleanup;
246 }
247
248 static const struct VscCliOption *
249 _option_get (const struct VscCliOption *option_list,
250              const char *option_long_name)
251 {
252         const struct VscCliOption *option;
253
254         VSC__ASSERT (option_long_name != NULL);
255
256         for (option = option_list; option != NULL; option = option->next) {
257                 if (strcmp (option->info->long_name, option_long_name) == 0) {
258                         return option;
259                 }
260         }
261
262         return NULL;
263 }
264
265 int
266 vsc_cli_option_get_bool (const struct VscCliOption *option_list,
267                          const char *option_long_name)
268 {
269         return _option_get (option_list, option_long_name) != NULL;
270 }
271
272 int
273 vsc_cli_option_get_number (const struct VscCliOption *option_list,
274                            const char *option_long_name, int *found)
275 {
276         const struct VscCliOption *option;
277
278         VSC__ASSERT (option_long_name != NULL);
279
280         if (found != NULL) {
281                 *found = TRUE;
282         }
283
284         option = _option_get (option_list, option_long_name);
285
286         if (option == NULL) {
287                 return 0;
288         }
289
290         if (found != NULL) {
291                 *found = TRUE;
292         }
293
294         return option->number;
295 }
296
297 const char *
298 vsc_cli_option_get_string (const struct VscCliOption *option_list,
299                            const char *option_long_name)
300 {
301         const struct VscCliOption *option;
302
303         VSC__ASSERT (option_long_name != NULL);
304
305         option = _option_get (option_list, option_long_name);
306
307         if (option == NULL) {
308                 return NULL;
309         }
310
311         return option->string;
312 }
313
314 const struct VscCliOptionInfo *
315 _vsc_cli_option_infos_lookup (const struct VscCliOptionInfo *option_infos,
316                               const char *option_name, int is_long)
317 {
318         int i;
319
320         VSC__ASSERT (option_infos != NULL);
321         VSC__ASSERT (option_name != NULL);
322
323         for (i = 0; option_infos[i].long_name != NULL; i++) {
324                 if (option_infos[i].type == VSC_CLI__OPTION_TYPE__DATA) {
325                         continue;
326                 }
327
328                 if (is_long) {
329                         if (strcmp (option_name, option_infos[i].long_name) == 0) {
330                                 return &option_infos[i];
331                         }
332                 } else {
333                         if (*option_name == option_infos[i].short_name) {
334                                 return &option_infos[i];
335                         }
336                 }
337         }
338
339         return NULL;
340 }
341
342 const struct VscCliOptionInfo *
343 _vsc_cli_option_infos_lookup_data (const struct VscCliOptionInfo *option_infos,
344                                    int position)
345 {
346         int i;
347
348         VSC__ASSERT (option_infos != NULL);
349         VSC__ASSERT (position >= 0);
350
351         for (i = 0; option_infos[i].long_name != NULL; i++) {
352                 if (option_infos[i].type == VSC_CLI__OPTION_TYPE__DATA) {
353                         if (position > 0) {
354                                 --position;
355                         } else {
356                                 return &option_infos[i];
357                         }
358                 }
359         }
360
361         return NULL;
362 }
363
364 static void
365 _option_free (struct VscCliOption **option)
366 {
367         VSC__ASSERT (option != NULL);
368
369         if (*option != NULL) {
370                 vsc_free (&(*option)->string);
371         }
372
373         vsc_free (option);
374 }
375
376 static struct VscCliOption * /* option_duplicate */
377 _option_duplicate (struct VscError *error, const struct VscCliOption *option)
378 {
379         struct VscCliOption *option_duplicate = NULL;
380
381         VSC__ASSERT (error != NULL);
382         VSC__ASSERT (! error->occured);
383         VSC__ASSERT (option != NULL);
384
385         option_duplicate = vsc_memdup (error, option, sizeof (struct VscCliOption));
386
387         if (error->occured) {
388                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
389                 goto failure;
390         }
391
392         if (option->string != NULL) {
393                 option_duplicate->string = vsc_strdup (error, option->string);
394         }
395
396         if (error->occured) {
397                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
398                 goto failure;
399         }
400
401         option_duplicate->next = NULL;
402
403         return option_duplicate;
404
405 failure:
406         _option_free (&option_duplicate);
407
408         return NULL;
409 }
410
411 static int
412 _option_match (struct VscList *item, void *match_data)
413 {
414         struct VscCliOption *option = (struct VscCliOption *) item;
415         const char *option_long_name = (const char *) match_data;
416
417         VSC__ASSERT (item != NULL);
418
419         return strcmp (option->info->long_name, option_long_name) == 0;
420 }
421
422 void
423 _vsc_cli_option_list_append (struct VscError *error,
424                              struct VscCliOption **option_list,
425                              const struct VscCliOption *option)
426 {
427         VSC__ASSERT (error != NULL);
428         VSC__ASSERT (! error->occured);
429         VSC__ASSERT (option_list != NULL);
430         VSC__ASSERT (option != NULL);
431
432         vsc_list_append
433            (error, (struct VscList **) option_list,
434             (struct VscList *) option,
435             (VscList_DuplicateFunction) _option_duplicate);
436
437         if (error->occured) {
438                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
439                 return;
440         }
441 }
442
443 void
444 vsc_cli_option_list_free (struct VscCliOption **option_list)
445 {
446         VSC__ASSERT (option_list != NULL);
447
448         vsc_list_free ((struct VscList **) option_list,
449                        (VscList_FreeFunction) _option_free);
450 }
451
452 struct VscCliOption * /* option_list_duplicate */
453 _vsc_cli_option_list_duplicate (struct VscError *error,
454                                 const struct VscCliOption *option_list)
455 {
456         struct VscCliOption *option_list_duplicate = NULL;
457
458         VSC__ASSERT (error != NULL);
459         VSC__ASSERT (! error->occured);
460
461         option_list_duplicate = (struct VscCliOption *)
462            vsc_list_duplicate
463               (error, (struct VscList *) option_list,
464                (VscList_DuplicateFunction) _option_duplicate,
465                (VscList_FreeFunction) _option_free);
466
467         if (error->occured) {
468                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
469                 return NULL;
470         }
471
472         return option_list_duplicate;
473 }
474
475 struct VscCliOption *
476 _vsc_cli_option_list_lookup (const struct VscCliOption *option_list,
477                              const char *option_long_name)
478 {
479         VSC__ASSERT (option_long_name != NULL);
480
481         return (struct VscCliOption *)
482                   vsc_list_lookup
483                      ((struct VscList *) option_list,
484                       (VscList_MatchFunction) _option_match,
485                       (void *) option_long_name);
486 }