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