command-line-arguments.[ch]: new argument parsing framework for notmuch.
[notmuch] / command-line-arguments.c
1 #include <assert.h>
2 #include <string.h>
3 #include <stdio.h>
4 #include "error_util.h"
5 #include "command-line-arguments.h"
6
7 /*
8   Search the array of keywords for a given argument, assigning the
9   output variable to the corresponding value.  Return FALSE if nothing
10   matches.
11 */
12
13 static notmuch_bool_t
14 _process_keyword_arg (const notmuch_opt_desc_t *arg_desc, const char *arg_str) {
15
16     notmuch_keyword_t *keywords = arg_desc->keywords;
17
18     while (keywords->name) {
19         if (strcmp (arg_str, keywords->name) == 0) {
20             if (arg_desc->output_var) {
21                 *((int *)arg_desc->output_var) = keywords->value;
22             }
23             return TRUE;
24         }
25         keywords++;
26     }
27     fprintf (stderr, "unknown keyword: %s\n", arg_str);
28     return FALSE;
29 }
30
31 /*
32    Search for the {pos_arg_index}th position argument, return FALSE if
33    that does not exist.
34 */
35
36 notmuch_bool_t
37 parse_position_arg (const char *arg_str, int pos_arg_index,
38                     const notmuch_opt_desc_t *arg_desc) {
39
40     int pos_arg_counter = 0;
41     while (arg_desc->opt_type != NOTMUCH_OPT_END){
42         if (arg_desc->opt_type == NOTMUCH_OPT_POSITION) {
43             if (pos_arg_counter == pos_arg_index) {
44                 if (arg_desc->output_var) {
45                     *((const char **)arg_desc->output_var) = arg_str;
46                 }
47                 return TRUE;
48             }
49             pos_arg_counter++;
50         }
51         arg_desc++;
52     }
53     return FALSE;
54 }
55
56 /*
57  * Search for a non-positional (i.e. starting with --) argument matching arg,
58  * parse a possible value, and assign to *output_var
59  */
60
61 notmuch_bool_t
62 parse_option (const char *arg,
63               const notmuch_opt_desc_t *options) {
64
65     assert(arg);
66     assert(options);
67
68     arg += 2;
69
70     const notmuch_opt_desc_t *try = options;
71     while (try->opt_type != NOTMUCH_OPT_END) {
72         if (try->name && strncmp (arg, try->name, strlen (try->name)) == 0) {
73             char next = arg[strlen (try->name)];
74             const char *value= arg+strlen(try->name)+1;
75
76             char *endptr;
77
78             /* Everything but boolean arguments (switches) needs a
79              * delimiter, and a non-zero length value
80              */
81
82             if (try->opt_type != NOTMUCH_OPT_BOOLEAN) {
83                 if (next != '=' && next != ':') return FALSE;
84                 if (value[0] == 0) return FALSE;
85             } else {
86                 if (next != 0) return FALSE;
87             }
88
89             if (try->output_var == NULL)
90                 INTERNAL_ERROR ("output pointer NULL for option %s", try->name);
91
92             switch (try->opt_type) {
93             case NOTMUCH_OPT_KEYWORD:
94                 return _process_keyword_arg (try, value);
95                 break;
96             case NOTMUCH_OPT_BOOLEAN:
97                 *((notmuch_bool_t *)try->output_var) = TRUE;
98                 return TRUE;
99                 break;
100             case NOTMUCH_OPT_INT:
101                 *((int *)try->output_var) = strtol (value, &endptr, 10);
102                 return (*endptr == 0);
103                 break;
104             case NOTMUCH_OPT_STRING:
105                 *((const char **)try->output_var) = value;
106                 return TRUE;
107                 break;
108             case NOTMUCH_OPT_POSITION:
109             case NOTMUCH_OPT_END:
110             default:
111                 INTERNAL_ERROR ("unknown or unhandled option type %d", try->opt_type);
112                 /*UNREACHED*/
113             }
114         }
115         try++;
116     }
117     fprintf (stderr, "Unrecognized option: --%s\n", arg);
118     return FALSE;
119 }
120
121 /* See command-line-arguments.h for description */
122 int
123 parse_arguments (int argc, char **argv,
124                  const notmuch_opt_desc_t *options, int opt_index) {
125
126     int pos_arg_index = 0;
127     notmuch_bool_t more_args = TRUE;
128
129     while (more_args && opt_index < argc) {
130         if (strncmp (argv[opt_index],"--",2) != 0) {
131
132             more_args = parse_position_arg (argv[opt_index], pos_arg_index, options);
133
134             if (more_args) {
135                 pos_arg_index++;
136                 opt_index++;
137             }
138
139         } else {
140
141             if (strlen (argv[opt_index]) == 2)
142                 return opt_index+1;
143
144             more_args = parse_option (argv[opt_index], options);
145             if (more_args) {
146                 opt_index++;
147             } else {
148                 opt_index = -1;
149             }
150
151         }
152     }
153
154     return opt_index;
155 }