]> git.ozlabs.org Git - ccan/blob - ccan/opt/parse.c
base64: fix for unsigned chars (e.g. ARM).
[ccan] / ccan / opt / parse.c
1 /* Licensed under GPLv2+ - see LICENSE file for details */
2 /* Actual code to parse commandline. */
3 #include <ccan/opt/opt.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <assert.h>
7 #include "private.h"
8
9 /* glibc does this as:
10 /tmp/opt-example: invalid option -- 'x'
11 /tmp/opt-example: unrecognized option '--long'
12 /tmp/opt-example: option '--someflag' doesn't allow an argument
13 /tmp/opt-example: option '--s' is ambiguous
14 /tmp/opt-example: option requires an argument -- 's'
15 */
16 static int parse_err(void (*errlog)(const char *fmt, ...),
17                      const char *argv0,
18                      const char *arg, unsigned len,
19                      const char *problem)
20 {
21         errlog("%s: %.*s: %s", argv0, len, arg, problem);
22         return -1;
23 }
24
25 static void consume_option(int *argc, char *argv[], unsigned optnum)
26 {
27         memmove(&argv[optnum], &argv[optnum+1],
28                 sizeof(argv[optnum]) * (*argc-optnum));
29         (*argc)--;
30 }
31
32 /* This sets the len and o to indicate how far it is into the
33  * opt_table's names field. */
34 static struct opt_table *opt_find_long_extra(const char *arg,
35                                              const char **optarg,
36                                              unsigned int *len,
37                                              const char **o)
38 {
39         unsigned i;
40
41         *optarg = NULL;
42         for (*o = first_lopt(&i, len);
43              *o;
44              *o = next_lopt(*o, &i, len)) {
45                 if (strncmp(arg, *o, *len) != 0)
46                         continue;
47                 if (arg[*len] == '=')
48                         *optarg = arg + *len + 1;
49                 else if (arg[*len] != '\0')
50                         continue;
51                 return &opt_table[i];
52
53         }
54         return NULL;
55 }
56
57 struct opt_table *opt_find_long(const char *arg, const char **optarg)
58 {
59         unsigned len;
60         const char *o;
61
62         return opt_find_long_extra(arg, optarg ? optarg : &o, &len, &o);
63 }
64
65 static struct opt_table *opt_find_short_extra(char arg, const char **o)
66 {
67         unsigned i;
68         for (*o = first_sopt(&i); *o; *o = next_sopt(*o, &i)) {
69                 if (arg == **o)
70                         return &opt_table[i];
71         }
72         return NULL;
73 }
74
75 struct opt_table *opt_find_short(char arg)
76 {
77         const char *o;
78         return opt_find_short_extra(arg, &o);
79 }
80
81 /* Returns 1 if argument consumed, 0 if all done, -1 on error. */
82 int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
83               void (*errlog)(const char *fmt, ...), bool unknown_ok)
84 {
85         unsigned arg, len;
86         const char *o, *optarg = NULL;
87         char *problem = NULL;
88         struct opt_table *ot;
89
90         if (getenv("POSIXLY_CORRECT")) {
91                 /* Don't find options after non-options. */
92                 arg = 1;
93         } else {
94                 for (arg = 1; argv[arg]; arg++) {
95                         if (argv[arg][0] == '-')
96                                 break;
97                 }
98         }
99
100         if (!argv[arg] || argv[arg][0] != '-')
101                 return 0;
102
103         /* Special arg terminator option. */
104         if (strcmp(argv[arg], "--") == 0) {
105                 consume_option(argc, argv, arg);
106                 return 0;
107         }
108
109         /* Long options start with -- */
110         if (argv[arg][1] == '-') {
111                 assert(*offset == 0);
112
113                 ot = opt_find_long_extra(argv[arg]+2, &optarg, &len, &o);
114                 if (!ot) {
115                         if (unknown_ok)
116                                 goto ok;
117                         return parse_err(errlog, argv[0],
118                                          argv[arg], strlen(argv[arg]),
119                                          "unrecognized option");
120                 }
121
122                 /* For error messages, we include the leading '--' */
123                 o -= 2;
124                 len += 2;
125         } else {
126                 ot = opt_find_short_extra(argv[arg][*offset + 1], &o);
127                 if (!ot) {
128                         if (unknown_ok) {
129                                 (*offset)++;
130                                 goto ok;
131                         }
132                         return parse_err(errlog, argv[0],
133                                          argv[arg], strlen(argv[arg]),
134                                          "unrecognized option");
135                 }
136
137                 (*offset)++;
138                 /* For error messages, we include the leading '-' */
139                 o--;
140                 len = 2;
141         }
142
143         if (ot->type & OPT_NOARG) {
144                 if (optarg)
145                         return parse_err(errlog, argv[0], o, len,
146                                          "doesn't allow an argument");
147                 if ((ot->type & OPT_EARLY) == is_early)
148                         problem = ot->cb(ot->u.arg);
149         } else {
150                 if (!optarg) {
151                         /* Swallow any short options as optarg, eg -afile */
152                         if (*offset && argv[arg][*offset + 1]) {
153                                 optarg = argv[arg] + *offset + 1;
154                                 *offset = 0;
155                         } else
156                                 optarg = argv[arg+1];
157                 }
158                 if (!optarg)
159                         return parse_err(errlog, argv[0], o, len,
160                                          "requires an argument");
161                 if ((ot->type & OPT_EARLY) == is_early)
162                         problem = ot->cb_arg(optarg, ot->u.arg);
163         }
164
165         if (problem) {
166                 parse_err(errlog, argv[0], o, len, problem);
167                 opt_alloc.free(problem);
168                 return -1;
169         }
170
171 ok:
172         /* If no more letters in that short opt, reset offset. */
173         if (*offset && !argv[arg][*offset + 1])
174                 *offset = 0;
175
176         /* All finished with that option? */
177         if (*offset == 0) {
178                 consume_option(argc, argv, arg);
179                 if (optarg && optarg == argv[arg])
180                         consume_option(argc, argv, arg);
181         }
182         return 1;
183 }