Mercurial > hg > index.cgi
annotate lwlib/lw_cmdline.c @ 547:33e37b3d98cf
Don't build snprintf() emulation on modern enough MSVC
MSVC 2015 and later have a C99 compliant snprintf() so don't build the
emulation function for it if building on a modern enough version.
author | William Astle <lost@l-w.ca> |
---|---|
date | Thu, 16 Mar 2023 16:18:13 -0600 |
parents | b138b4005125 |
children |
rev | line source |
---|---|
4 | 1 /* |
2 lw_cmdline.c | |
3 | |
4 Copyright © 2010 William Astle | |
5 | |
6 This file is part of LWTOOLS. | |
7 | |
8 LWTOOLS is free software: you can redistribute it and/or modify it under the | |
9 terms of the GNU General Public License as published by the Free Software | |
10 Foundation, either version 3 of the License, or (at your option) any later | |
11 version. | |
12 | |
13 This program is distributed in the hope that it will be useful, but WITHOUT | |
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
16 more details. | |
17 | |
18 You should have received a copy of the GNU General Public License along with | |
19 this program. If not, see <http://www.gnu.org/licenses/>. | |
20 */ | |
21 | |
441
b138b4005125
Make missing command line arguments fail properly
William Astle <lost@l-w.ca>
parents:
374
diff
changeset
|
22 #include <errno.h> |
4 | 23 #include <stdio.h> |
24 #include <stdlib.h> | |
25 #include <string.h> | |
8
fdc11ef4115b
Switched lwlink to lw_cmdline from argp and also brought in lw_alloc and lw_string to replace util.c
lost@l-w.ca
parents:
6
diff
changeset
|
26 #include <ctype.h> |
4 | 27 |
28 #include "lw_alloc.h" | |
29 #include "lw_cmdline.h" | |
30 | |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
31 #define DOCCOL 30 |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
32 #define LLEN 78 |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
33 |
4 | 34 static struct lw_cmdline_options builtin[3] = |
35 { | |
36 { "help", '?', 0, 0, "give this help list" }, | |
37 { "usage", 0, 0, 0, "give a short usage message" }, | |
38 { "version", 'V', 0, 0, "print program version" } | |
39 }; | |
40 | |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
41 static int cmpr(const void *e1, const void *e2) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
42 { |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
43 struct lw_cmdline_options **o1, **o2; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
44 o1 = (struct lw_cmdline_options **)e1; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
45 o2 = (struct lw_cmdline_options **)e2; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
46 |
191
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
47 return strcmp((*o1) -> name ? (*o1) -> name : "", (*o2) -> name ? (*o2) -> name : ""); |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
48 } |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
49 |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
50 static int cmpr2(const void *e1, const void *e2) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
51 { |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
52 struct lw_cmdline_options **o1, **o2; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
53 o1 = (struct lw_cmdline_options **)e1; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
54 o2 = (struct lw_cmdline_options **)e2; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
55 |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
56 if ((*o1) -> key < ((*o2) -> key)) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
57 return -1; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
58 if ((*o1) -> key > ((*o2) -> key)) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
59 return 1; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
60 return 0; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
61 } |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
62 |
4 | 63 static void lw_cmdline_usage(struct lw_cmdline_parser *parser, char *name) |
64 { | |
65 struct lw_cmdline_options **slist, **llist; | |
66 int nopt; | |
67 int i; | |
68 int t; | |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
69 int col; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
70 |
191
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
71 for (nopt = 0; parser -> options[nopt].name || parser -> options[nopt].key || parser -> options[nopt].doc; nopt++) |
4 | 72 /* do nothing */ ; |
73 | |
74 slist = lw_alloc(sizeof(struct lw_cmdline_options *) * (nopt + 3)); | |
75 llist = lw_alloc(sizeof(struct lw_cmdline_options *) * (nopt + 3)); | |
76 | |
77 for (i = 0; i < nopt; i++) | |
78 { | |
79 slist[i] = &(parser -> options[i]); | |
80 llist[i] = &(parser -> options[i]); | |
81 } | |
82 | |
83 /* now sort the two lists */ | |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
84 qsort(slist, nopt, sizeof(struct lw_cmdline_options *), cmpr2); |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
85 qsort(llist, nopt, sizeof(struct lw_cmdline_options *), cmpr); |
4 | 86 |
87 /* now append the automatic options */ | |
88 slist[nopt] = &(builtin[0]); | |
89 slist[nopt + 1] = &(builtin[1]); | |
90 slist[nopt + 2] = &(builtin[2]); | |
91 | |
92 llist[nopt] = &(builtin[0]); | |
93 llist[nopt + 1] = &(builtin[1]); | |
94 llist[nopt + 2] = &(builtin[2]); | |
95 | |
96 /* now show the usage message */ | |
97 printf("Usage: %s", name); | |
98 | |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
99 col = 7 + strlen(name); |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
100 |
4 | 101 /* print short options that take no args */ |
102 t = 0; | |
103 for (i = 0; i < nopt + 3; i++) | |
104 { | |
192 | 105 if (slist[i]->flags & lw_cmdline_opt_hidden) |
106 continue; | |
4 | 107 if (slist[i]->key > 0x20 && slist[i]->key < 0x7f) |
108 { | |
109 if (slist[i]->arg == NULL) | |
110 { | |
111 if (!t) | |
112 { | |
113 printf(" [-"); | |
114 t = 1; | |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
115 col += 3; |
4 | 116 } |
117 printf("%c", slist[i]->key); | |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
118 col++; |
4 | 119 } |
120 } | |
121 } | |
122 if (t) | |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
123 { |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
124 col++; |
4 | 125 printf("]"); |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
126 } |
4 | 127 |
128 /* print short options that take args */ | |
129 for (i = 0; i < nopt + 3; i++) | |
130 { | |
192 | 131 if (slist[i]->flags & lw_cmdline_opt_hidden) |
132 continue; | |
4 | 133 if (slist[i]->key > 0x20 && slist[i]->key < 0x7f && slist[i] -> arg) |
134 { | |
135 if (slist[i]->flags & lw_cmdline_opt_optional) | |
136 { | |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
137 t = 7 + strlen(slist[i] -> arg); |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
138 if (col + t > LLEN) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
139 { |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
140 printf("\n "); |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
141 col = 7; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
142 } |
4 | 143 printf(" [-%c[%s]]", slist[i]->key, slist[i]->arg); |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
144 col += t; |
4 | 145 } |
146 else | |
147 { | |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
148 t = 6 + strlen(slist[i] -> arg); |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
149 if (col + t > LLEN) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
150 { |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
151 printf("\n "); |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
152 col = 7; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
153 } |
4 | 154 printf(" [-%c %s]", slist[i]->key, slist[i]->arg); |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
155 col += t; |
4 | 156 } |
157 } | |
158 } | |
159 | |
160 /* print long options */ | |
161 for (i = 0; i < nopt + 3; i++) | |
162 { | |
192 | 163 if (slist[i]->flags & lw_cmdline_opt_hidden) |
164 continue; | |
191
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
165 if (!(llist[i]->name)) |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
166 continue; |
4 | 167 if (llist[i]->arg) |
168 { | |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
169 t = strlen(llist[i] -> name) + 6 + strlen(llist[i] -> arg); |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
170 if (llist[i] -> flags & lw_cmdline_opt_optional) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
171 t += 2; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
172 if (col + t > LLEN) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
173 { |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
174 printf("\n "); |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
175 col = 7; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
176 } |
191
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
177 if (llist[i] -> flags & lw_cmdline_opt_doc) |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
178 { |
198
d2bed389e94a
Fix --help and --usage to display = correctly for long option args
William Astle <lost@l-w.ca>
parents:
192
diff
changeset
|
179 printf(" [%s=%s]", llist[i] -> name, llist[i] -> arg); |
191
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
180 t = strlen(llist[i] -> name) + strlen(llist[i] -> arg) + 3; |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
181 } |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
182 else |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
183 { |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
184 printf(" [--%s%s=%s%s]", |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
185 llist[i] -> name, |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
186 (llist[i] -> flags & lw_cmdline_opt_optional) ? "[" : "", |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
187 llist[i] -> arg, |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
188 (llist[i] -> flags & lw_cmdline_opt_optional) ? "]" : ""); |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
189 } |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
190 col += t; |
4 | 191 } |
192 else | |
193 { | |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
194 t = strlen(llist[i] -> name) + 5; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
195 if (col + t > LLEN) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
196 { |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
197 printf("\n "); |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
198 col = 7; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
199 } |
191
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
200 if (llist[i] -> flags & lw_cmdline_opt_doc) |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
201 { |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
202 t -= 2; |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
203 printf(" [%s]", llist[i] -> name); |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
204 } |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
205 else |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
206 { |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
207 printf(" [--%s]", llist[i] -> name); |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
208 } |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
209 col += t; |
4 | 210 } |
211 } | |
212 | |
213 /* print "non option" text */ | |
214 if (parser -> args_doc) | |
215 { | |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
216 if (col + strlen(parser -> args_doc) + 1 > LLEN) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
217 { |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
218 printf("\n "); |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
219 } |
4 | 220 printf(" %s", parser -> args_doc); |
221 } | |
222 printf("\n"); | |
223 | |
224 /* clean up scratch lists */ | |
225 lw_free(slist); | |
226 lw_free(llist); | |
227 } | |
228 | |
5 | 229 static void lw_cmdline_help(struct lw_cmdline_parser *parser, char *name) |
4 | 230 { |
5 | 231 struct lw_cmdline_options **llist; |
232 int nopt; | |
233 int i; | |
234 char *tstr; | |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
235 int col = 0; |
191
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
236 int noequ; |
5 | 237 |
238 tstr = parser -> doc; | |
191
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
239 for (nopt = 0; parser -> options[nopt].name || parser -> options[nopt].key || parser -> options[nopt].doc; nopt++) |
5 | 240 /* do nothing */ ; |
241 | |
242 llist = lw_alloc(sizeof(struct lw_cmdline_options *) * (nopt + 3)); | |
243 | |
244 for (i = 0; i < nopt; i++) | |
245 { | |
246 llist[i] = &(parser -> options[i]); | |
247 } | |
248 | |
249 /* now sort the list */ | |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
250 qsort(llist, nopt, sizeof(struct lw_cmdline_options *), cmpr); |
5 | 251 |
252 /* now append the automatic options */ | |
253 llist[nopt] = &(builtin[0]); | |
254 llist[nopt + 1] = &(builtin[1]); | |
255 llist[nopt + 2] = &(builtin[2]); | |
256 | |
257 /* print brief usage */ | |
258 printf("Usage: %s [OPTION...] %s\n", name, parser -> args_doc ? parser -> args_doc : ""); | |
259 if (tstr) | |
260 { | |
261 while (*tstr && *tstr != '\v') | |
262 fputc(*tstr++, stdout); | |
263 if (*tstr) | |
264 tstr++; | |
265 } | |
266 fputc('\n', stdout); | |
267 fputc('\n', stdout); | |
268 | |
269 /* display options - do it the naïve way for now */ | |
270 for (i = 0; i < (nopt + 3); i++) | |
271 { | |
192 | 272 if (llist[i]->flags & lw_cmdline_opt_hidden) |
273 continue; | |
191
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
274 noequ = 0; |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
275 if (llist[i] -> flags & lw_cmdline_opt_doc) |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
276 { |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
277 col = strlen(llist[i] -> name) + 2; |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
278 printf(" %s", llist[i] -> name); |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
279 noequ = 1; |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
280 } |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
281 else if (llist[i] -> key > 0x20 && llist[i] -> key < 0x7F) |
5 | 282 { |
191
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
283 printf(" -%c", llist[i] -> key); |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
284 col = 5; |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
285 if (llist[i] -> name) |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
286 { |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
287 col++; |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
288 fputc(',', stdout); |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
289 } |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
290 fputc(' ', stdout); |
5 | 291 } |
292 else | |
293 { | |
294 printf(" "); | |
191
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
295 col = 6; |
5 | 296 } |
191
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
297 if (llist[i] -> name && !(llist[i] -> flags & lw_cmdline_opt_doc)) |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
298 { |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
299 col += 2 + strlen(llist[i] -> name); |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
300 printf("--%s", llist[i] -> name); |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
301 } |
5 | 302 if (llist[i] -> arg) |
303 { | |
304 if (llist[i] -> flags & lw_cmdline_opt_optional) | |
305 { | |
191
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
306 col++; |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
307 fputc('[', stdout); |
5 | 308 } |
198
d2bed389e94a
Fix --help and --usage to display = correctly for long option args
William Astle <lost@l-w.ca>
parents:
192
diff
changeset
|
309 if (!noequ) |
5 | 310 { |
191
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
311 fputc('=', stdout); |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
312 col++; |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
313 } |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
314 printf("%s", llist[i] -> arg); |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
315 col += strlen(llist[i] -> arg); |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
316 if (llist[i] -> flags & lw_cmdline_opt_optional) |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
317 { |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
318 col++; |
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
319 fputc(']', stdout); |
5 | 320 } |
321 } | |
322 if (llist[i] -> doc) | |
323 { | |
6
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
324 char *s = llist[i] -> doc; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
325 char *s2; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
326 |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
327 while (*s && isspace(*s)) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
328 s++; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
329 |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
330 if (col > DOCCOL) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
331 { |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
332 fputc('\n', stdout); |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
333 col = 0; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
334 } |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
335 while (*s) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
336 { |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
337 while (col < (DOCCOL - 1)) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
338 { |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
339 fputc(' ', stdout); |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
340 col++; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
341 } |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
342 for (s2 = s; *s2 && !isspace(*s2); s2++) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
343 /* do nothing */ ; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
344 if ((col + (s2 - s) + 1) > LLEN && col >= DOCCOL) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
345 { |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
346 /* next line */ |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
347 fputc('\n', stdout); |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
348 col = 0; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
349 continue; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
350 } |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
351 col++; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
352 fputc(' ', stdout); |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
353 while (s != s2) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
354 { |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
355 fputc(*s, stdout); |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
356 col++; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
357 s++; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
358 } |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
359 while (*s && isspace(*s)) |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
360 s++; |
1e5e8ec406fb
Various output cleanups for --help and --usage in lw_cmdline
lost@l-w.ca
parents:
5
diff
changeset
|
361 } |
5 | 362 } |
363 fputc('\n', stdout); | |
364 } | |
365 | |
366 printf("\nMandatory or optional arguments to long options are also mandatory or optional\nfor any corresponding short options.\n"); | |
367 | |
368 if (*tstr) | |
369 { | |
370 printf("\n%s\n", tstr); | |
371 } | |
372 | |
373 /* clean up scratch lists */ | |
374 lw_free(llist); | |
4 | 375 } |
376 | |
377 int lw_cmdline_parse(struct lw_cmdline_parser *parser, int argc, char **argv, unsigned flags, int *arg_index, void *input) | |
378 { | |
379 int i, j, r; | |
380 int firstarg; | |
381 int nextarg; | |
382 char *tstr; | |
383 int cch; | |
441
b138b4005125
Make missing command line arguments fail properly
William Astle <lost@l-w.ca>
parents:
374
diff
changeset
|
384 |
4 | 385 /* first, permute the argv array so that all option arguments are at the start */ |
386 for (i = 1, firstarg = 1; i < argc; i++) | |
387 { | |
388 if (argv[i][0] == '-' && argv[i][1]) | |
389 { | |
390 /* have an option arg */ | |
391 if (firstarg == i) | |
392 { | |
393 firstarg++; | |
394 continue; | |
395 } | |
396 tstr = argv[i]; | |
397 for (j = i; j > firstarg; j--) | |
398 { | |
399 argv[j] = argv[j - 1]; | |
400 } | |
401 argv[firstarg] = tstr; | |
402 firstarg++; | |
403 if (argv[firstarg - 1][1] == '-' && argv[firstarg - 1][2] == 0) | |
404 break; | |
405 } | |
406 } | |
407 | |
408 /* now start parsing options */ | |
409 nextarg = firstarg; | |
410 i = 1; | |
411 cch = 0; | |
412 while (i < firstarg) | |
413 { | |
414 if (cch > 0 && argv[i][cch] == 0) | |
415 { | |
416 i++; | |
417 cch = 0; | |
418 continue; | |
419 } | |
420 | |
421 if (cch > 0) | |
422 goto shortopt; | |
423 | |
424 /* skip the "--" option */ | |
425 if (argv[i][1] == '-' && argv[i][2] == 0) | |
426 break; | |
427 | |
428 if (argv[i][1] == '-') | |
429 { | |
430 goto longopt; | |
431 } | |
432 | |
433 cch = 1; | |
434 shortopt: | |
435 /* handle a short option here */ | |
436 | |
437 /* automatic options */ | |
438 if (argv[i][cch] == '?') | |
439 goto do_help; | |
440 if (argv[i][cch] == 'V') | |
441 goto do_version; | |
442 /* look up key */ | |
191
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
443 for (j = 0; parser -> options[j].name || parser -> options[j].key || parser -> options[j].doc; j++) |
4 | 444 if (parser -> options[j].key == argv[i][cch]) |
445 break; | |
446 cch++; | |
447 tstr = argv[i] + cch; | |
105 | 448 if (*tstr == 0) |
449 tstr = NULL; | |
450 if (!tstr && (parser -> options[j].flags & lw_cmdline_opt_optional) == 0) | |
4 | 451 { |
103
8b0be0fc42cf
Fixed arg handling for short args in command line option parser - optional args for short opts should now work and options no longer need a space between option character and argument
lost@l-w.ca
parents:
54
diff
changeset
|
452 /* only consume the next arg if the argument is optional */ |
4 | 453 if (nextarg < argc) |
454 tstr = argv[nextarg]; | |
455 else | |
456 tstr = NULL; | |
457 } | |
458 goto common; | |
459 | |
460 longopt: | |
461 if (strcmp(argv[i], "--help") == 0) | |
462 goto do_help; | |
463 if (strcmp(argv[i], "--usage") == 0) | |
464 goto do_usage; | |
465 if (strcmp(argv[i], "--version") == 0) | |
466 goto do_version; | |
467 /* look up name */ | |
468 | |
469 for (j = 2; argv[i][j] && argv[i][j] != '='; j++) | |
470 /* do nothing */ ; | |
471 tstr = lw_alloc(j - 1); | |
472 strncpy(tstr, argv[i] + 2, j - 2); | |
52 | 473 tstr[j - 2] = 0; |
4 | 474 if (argv[i][j] == '=') |
475 j++; | |
476 cch = j; | |
191
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
477 for (j = 0; parser -> options[j].name || parser -> options[j].key || parser -> options[j].doc; j++) |
4 | 478 { |
191
ddffceb3c331
Added "documentation only" options to lw_cmdline and also allowed options that do not have a long equivalent
lost@l-w.ca
parents:
105
diff
changeset
|
479 if (parser -> options[j].name && strcmp(parser -> options[j].name, tstr) == 0) |
4 | 480 break; |
481 } | |
482 lw_free(tstr); | |
483 tstr = argv[i] + cch; | |
105 | 484 if (*tstr == 0) |
485 tstr = NULL; | |
4 | 486 cch = 0; |
37 | 487 i++; |
4 | 488 |
489 common: | |
490 /* j will be the offset into the option table when we get here */ | |
491 /* cch will be zero and tstr will point to the arg if it's a long option */ | |
492 /* cch will be > 0 and tstr points to the theoretical option, either within */ | |
493 /* this string or "nextarg" */ | |
494 if (parser -> options[j].name == NULL) | |
495 { | |
103
8b0be0fc42cf
Fixed arg handling for short args in command line option parser - optional args for short opts should now work and options no longer need a space between option character and argument
lost@l-w.ca
parents:
54
diff
changeset
|
496 if (cch) |
8b0be0fc42cf
Fixed arg handling for short args in command line option parser - optional args for short opts should now work and options no longer need a space between option character and argument
lost@l-w.ca
parents:
54
diff
changeset
|
497 fprintf(stderr, "Unknown option '%c'. See %s --usage.\n", argv[i][cch - 1], argv[0]); |
8b0be0fc42cf
Fixed arg handling for short args in command line option parser - optional args for short opts should now work and options no longer need a space between option character and argument
lost@l-w.ca
parents:
54
diff
changeset
|
498 else |
8b0be0fc42cf
Fixed arg handling for short args in command line option parser - optional args for short opts should now work and options no longer need a space between option character and argument
lost@l-w.ca
parents:
54
diff
changeset
|
499 fprintf(stderr, "Unknown option '%s'. See %s --usage.\n", argv[i - 1], argv[0]); |
441
b138b4005125
Make missing command line arguments fail properly
William Astle <lost@l-w.ca>
parents:
374
diff
changeset
|
500 return EINVAL; |
4 | 501 } |
502 if (parser -> options[j].arg) | |
503 { | |
504 if (tstr && cch && argv[i][cch] == 0) | |
505 nextarg++; | |
506 | |
105 | 507 //if (!*tstr) |
508 // tstr = NULL; | |
4 | 509 |
103
8b0be0fc42cf
Fixed arg handling for short args in command line option parser - optional args for short opts should now work and options no longer need a space between option character and argument
lost@l-w.ca
parents:
54
diff
changeset
|
510 /* move on to next argument if we have an arg specified */ |
8b0be0fc42cf
Fixed arg handling for short args in command line option parser - optional args for short opts should now work and options no longer need a space between option character and argument
lost@l-w.ca
parents:
54
diff
changeset
|
511 if (tstr && cch && argv[i][cch] != 0) |
104 | 512 { |
103
8b0be0fc42cf
Fixed arg handling for short args in command line option parser - optional args for short opts should now work and options no longer need a space between option character and argument
lost@l-w.ca
parents:
54
diff
changeset
|
513 i++; |
104 | 514 cch = 0; |
515 } | |
103
8b0be0fc42cf
Fixed arg handling for short args in command line option parser - optional args for short opts should now work and options no longer need a space between option character and argument
lost@l-w.ca
parents:
54
diff
changeset
|
516 |
4 | 517 if (!tstr && (parser -> options[j].flags & lw_cmdline_opt_optional) == 0) |
518 { | |
519 fprintf(stderr, "Option %s requires argument.\n", parser -> options[j].name); | |
441
b138b4005125
Make missing command line arguments fail properly
William Astle <lost@l-w.ca>
parents:
374
diff
changeset
|
520 return EINVAL; |
4 | 521 } |
522 } | |
523 r = (*(parser -> parser))(parser -> options[j].key, tstr, input); | |
524 if (r != 0) | |
525 return r; | |
526 } | |
527 /* handle non-option args */ | |
528 if (arg_index) | |
529 *arg_index = nextarg; | |
530 for (i = nextarg; i < argc; i++) | |
531 { | |
532 r = (*(parser -> parser))(lw_cmdline_key_arg, argv[i], input); | |
533 if (r != 0) | |
534 return r; | |
535 } | |
536 r = (*(parser -> parser))(lw_cmdline_key_end, NULL, input); | |
537 return r; | |
538 | |
539 do_help: | |
5 | 540 lw_cmdline_help(parser, argv[0]); |
4 | 541 exit(0); |
542 | |
543 do_version: | |
544 printf("%s\n", parser -> program_version); | |
545 exit(0); | |
546 | |
547 do_usage: | |
548 lw_cmdline_usage(parser, argv[0]); | |
549 exit(0); | |
550 } |