Mercurial > hg > index.cgi
annotate lwlib/lw_cmdline.c @ 577:e49d24f4a9a5
Correct bug in the object file output code leading to stack corruption
It turns out leaving a pointer to a stack allocated temporary in a
persistent data structure is not conducive to correct program operation.
Undo the export check setup in the object file output sequence so a
pointer to stack allocated memory is not left hanging when the function
returns. This seems to correct at least one mysterious crash bug, and
possibly others.
Thanks to Boisy Pitre for reporting the crash bug that led to this
discovery, as well as a previous crash bug that likely has the same
root cause.
Additional thanks to Ciaran Anscomb whose debugger wielding wizardry
revealed the exact location of this particular bit of unbrilliance.
author | William Astle <lost@l-w.ca> |
---|---|
date | Sat, 03 Aug 2024 14:30:06 -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 } |