author | Jon Tibble <meths@btinternet.com> |
Thu, 09 Dec 2010 22:32:39 +0100 | |
changeset 13255 | 4afa820d78b9 |
parent 9664 | 3ab9bde9a605 |
permissions | -rw-r--r-- |
0 | 1 |
/* |
9664
3ab9bde9a605
PSARC 2009/228 ls enhancements
Jason King <jason@ansipunx.net>
parents:
6812
diff
changeset
|
2 |
* Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
0 | 3 |
* Use is subject to license terms. |
4 |
*/ |
|
5 |
||
6 |
/* |
|
7 |
* Copyright (c) 2002 Todd C. Miller <[email protected]> |
|
8 |
* All rights reserved. |
|
9 |
* |
|
10 |
* Redistribution and use in source and binary forms, with or without |
|
11 |
* modification, are permitted provided that the following conditions |
|
12 |
* are met: |
|
13 |
* 1. Redistributions of source code must retain the above copyright |
|
14 |
* notice, this list of conditions and the following disclaimer. |
|
15 |
* 2. Redistributions in binary form must reproduce the above copyright |
|
16 |
* notice, this list of conditions and the following disclaimer in the |
|
17 |
* documentation and/or other materials provided with the distribution. |
|
18 |
* 3. The name of the author may not be used to endorse or promote products |
|
19 |
* derived from this software without specific prior written permission. |
|
20 |
* |
|
21 |
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, |
|
22 |
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
|
23 |
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
|
24 |
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|
25 |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
26 |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
|
27 |
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
|
28 |
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
|
29 |
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
|
30 |
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
31 |
*/ |
|
32 |
/* |
|
33 |
* Copyright (c) 2000 The NetBSD Foundation, Inc. |
|
34 |
* All rights reserved. |
|
35 |
* |
|
36 |
* This code is derived from software contributed to The NetBSD Foundation |
|
37 |
* by Dieter Baron and Thomas Klausner. |
|
38 |
* |
|
39 |
* Redistribution and use in source and binary forms, with or without |
|
40 |
* modification, are permitted provided that the following conditions |
|
41 |
* are met: |
|
42 |
* 1. Redistributions of source code must retain the above copyright |
|
43 |
* notice, this list of conditions and the following disclaimer. |
|
44 |
* 2. Redistributions in binary form must reproduce the above copyright |
|
45 |
* notice, this list of conditions and the following disclaimer in the |
|
46 |
* documentation and/or other materials provided with the distribution. |
|
47 |
* 3. All advertising materials mentioning features or use of this software |
|
48 |
* must display the following acknowledgement: |
|
49 |
* This product includes software developed by the NetBSD |
|
50 |
* Foundation, Inc. and its contributors. |
|
51 |
* 4. Neither the name of The NetBSD Foundation nor the names of its |
|
52 |
* contributors may be used to endorse or promote products derived |
|
53 |
* from this software without specific prior written permission. |
|
54 |
* |
|
55 |
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
|
56 |
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
|
57 |
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
58 |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
|
59 |
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
60 |
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
61 |
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
62 |
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
63 |
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
64 |
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
65 |
* POSSIBILITY OF SUCH DAMAGE. |
|
66 |
*/ |
|
67 |
||
6812 | 68 |
#pragma weak _getopt_clip = getopt_clip |
69 |
#pragma weak _getopt_long = getopt_long |
|
70 |
#pragma weak _getopt_long_only = getopt_long_only |
|
71 |
||
72 |
#include "lint.h" |
|
0 | 73 |
#include <getopt.h> |
74 |
#include <stdio.h> |
|
75 |
#include <errno.h> |
|
76 |
#include <unistd.h> |
|
77 |
#include <stdlib.h> |
|
78 |
#include <string.h> |
|
79 |
#include "_libc_gettext.h" |
|
80 |
||
81 |
static int optreset = 0; /* keep track of first entry to getopt() */ |
|
82 |
#define PRINT_ERROR ((opterr) && (*options != ':')) |
|
83 |
#define FLAG_IS_SET(flag) ((flags & flag) != 0) /* is flag turned on? */ |
|
84 |
||
85 |
/* Determine if an argument is required for this long option */ |
|
86 |
#define LONGOPT_REQUIRES_ARG(longopt) \ |
|
87 |
((((longopt).has_arg == optional_argument) && \ |
|
88 |
(!FLAG_IS_SET(FLAG_OPTIONAL_ARGS))) || \ |
|
89 |
((longopt).has_arg == required_argument)) |
|
90 |
||
91 |
#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ |
|
92 |
#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "1" */ |
|
93 |
#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only() */ |
|
94 |
#define FLAG_OPTIONAL_ARGS 0x08 /* allow optional arguments to options */ |
|
95 |
#define FLAG_REQUIRE_EQUIVALENTS 0x10 /* require short<->long equivalents */ |
|
96 |
#define FLAG_ABBREV 0x20 /* long option abbreviation allowed. */ |
|
97 |
#define FLAG_W_SEMICOLON 0x40 /* Support for W; in optstring */ |
|
98 |
#define FLAG_PLUS_DASH_START 0x80 /* leading '+' or '-' in optstring */ |
|
99 |
||
100 |
/* return values */ |
|
101 |
#define BADCH (int)'?' |
|
102 |
#define BADARG ((*options == ':') ? (int)':' : (int)'?') |
|
103 |
#define INORDER (int)1 |
|
104 |
||
105 |
#define EMSG "" |
|
106 |
||
107 |
static int getopt_internal(int, char * const *, const char *, |
|
108 |
const struct option *, int *, uint_t); |
|
109 |
static int parse_long_options(int nargc, char * const *nargv, const char *, |
|
110 |
const struct option *, int *, int, |
|
111 |
uint_t flags); |
|
112 |
static int gcd(int, int); |
|
113 |
static void permute_args(int, int, int, char * const *); |
|
114 |
||
115 |
static char *place = EMSG; /* option letter processing */ |
|
116 |
||
117 |
/* XXX: set optreset to 1 rather than these two */ |
|
118 |
static int nonopt_start = -1; /* first non option argument (for permute) */ |
|
119 |
static int nonopt_end = -1; /* first option after non options (for permute) */ |
|
120 |
||
121 |
/* |
|
122 |
* Generalized error message output. |
|
123 |
* |
|
124 |
* NOTE ON ERROR MESSAGES: All the error messages in this file |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
125 |
* use %s (not %c) because they are all routed through warnx_getopt(), |
0 | 126 |
* which takes a string argument. Character arguments passed |
127 |
* to warnxchar() are converted to strings automatically before |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
128 |
* being passed to warnx_getopt(). |
0 | 129 |
*/ |
130 |
static void |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
131 |
warnx_getopt(const char *argv0, const char *msg, const char *arg) { |
0 | 132 |
char errbuf[256]; |
133 |
(void) snprintf(errbuf, sizeof (errbuf), msg, argv0, arg); |
|
134 |
(void) write(2, errbuf, strlen(errbuf)); |
|
135 |
(void) write(2, "\n", 1); |
|
136 |
} |
|
137 |
||
138 |
/* |
|
139 |
* Generalized error message output. |
|
140 |
*/ |
|
141 |
static void |
|
142 |
warnxchar(const char *argv0, const char *msg, const char c) { |
|
143 |
char charbuf[2]; |
|
144 |
charbuf[0] = c; |
|
145 |
charbuf[1] = '\0'; |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
146 |
warnx_getopt(argv0, msg, charbuf); |
0 | 147 |
} |
148 |
||
149 |
/* |
|
150 |
* Generalized error message output. |
|
151 |
*/ |
|
152 |
static void |
|
153 |
warnxlen(const char *argv0, const char *msg, int argLen, const char *arg) { |
|
154 |
char argbuf[256]; |
|
155 |
(void) strncpy(argbuf, arg, argLen); |
|
156 |
argbuf[argLen < (sizeof (argbuf)-1)? argLen:(sizeof (argbuf)-1)] = '\0'; |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
157 |
warnx_getopt(argv0, msg, argbuf); |
0 | 158 |
} |
159 |
||
160 |
/* |
|
161 |
* Compute the greatest common divisor of a and b. |
|
162 |
*/ |
|
163 |
static int |
|
164 |
gcd(int a, int b) |
|
165 |
{ |
|
166 |
int c; |
|
167 |
||
168 |
c = a % b; |
|
169 |
while (c != 0) { |
|
170 |
a = b; |
|
171 |
b = c; |
|
172 |
c = a % b; |
|
173 |
} |
|
174 |
||
175 |
return (b); |
|
176 |
} |
|
177 |
||
178 |
/* |
|
179 |
* Exchange the block from nonopt_start to nonopt_end with the block |
|
180 |
* from nonopt_end to opt_end (keeping the same order of arguments |
|
181 |
* in each block). |
|
182 |
*/ |
|
183 |
static void |
|
184 |
permute_args(int panonopt_start, int panonopt_end, int opt_end, |
|
185 |
char * const *nargv) |
|
186 |
{ |
|
187 |
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; |
|
188 |
char *swap; |
|
189 |
||
190 |
/* |
|
191 |
* compute lengths of blocks and number and size of cycles |
|
192 |
*/ |
|
193 |
nnonopts = panonopt_end - panonopt_start; |
|
194 |
nopts = opt_end - panonopt_end; |
|
195 |
ncycle = gcd(nnonopts, nopts); |
|
196 |
cyclelen = (opt_end - panonopt_start) / ncycle; |
|
197 |
||
198 |
for (i = 0; i < ncycle; i++) { |
|
199 |
cstart = panonopt_end+i; |
|
200 |
pos = cstart; |
|
201 |
for (j = 0; j < cyclelen; j++) { |
|
202 |
if (pos >= panonopt_end) |
|
203 |
pos -= nnonopts; |
|
204 |
else |
|
205 |
pos += nopts; |
|
206 |
swap = nargv[pos]; |
|
207 |
((char **)nargv)[pos] = nargv[cstart]; |
|
208 |
((char **)nargv)[cstart] = swap; |
|
209 |
} |
|
210 |
} |
|
211 |
} /* permute_args() */ |
|
212 |
||
213 |
/* |
|
214 |
* Verify that each short option (character flag) has a long equivalent, |
|
215 |
* and that each long option has a short option equivalent. Note that |
|
216 |
* multiple long options can map to the same character. |
|
217 |
* |
|
218 |
* This behavior is defined by Sun's CLIP specification (11/12/02), |
|
219 |
* and currently getopt_clip() is the only getopt variant that |
|
220 |
* requires it. |
|
221 |
* |
|
222 |
* If error output is enabled and an error is found, this function |
|
223 |
* prints ONE error message (the first error found) and returns an |
|
224 |
* error value. |
|
225 |
* |
|
226 |
* ASSUMES: options != NULL |
|
227 |
* ASSUMES: long_options may be NULL |
|
228 |
* |
|
229 |
* Returns < 0 if an error is found |
|
230 |
* Returns >= 0 on success |
|
231 |
*/ |
|
232 |
static int |
|
233 |
/* LINTED: argument unused in function: nargc */ |
|
234 |
verify_short_long_equivalents(int nargc, |
|
235 |
char *const *nargv, |
|
236 |
const char *options, |
|
237 |
const struct option *long_options, |
|
238 |
uint_t flags) { |
|
239 |
int short_i = 0; |
|
240 |
int long_i = 0; |
|
241 |
int equivFound = 0; |
|
242 |
int ch = 0; |
|
243 |
||
244 |
/* |
|
245 |
* Find a long option for each short option |
|
246 |
*/ |
|
247 |
equivFound = 1; |
|
248 |
for (short_i = 0; equivFound && (options[short_i] != 0); ++short_i) { |
|
249 |
ch = options[short_i]; |
|
250 |
||
251 |
if (ch == ':') { |
|
252 |
continue; |
|
253 |
} |
|
254 |
if (FLAG_IS_SET(FLAG_W_SEMICOLON) && |
|
255 |
(ch == 'W') && (options[short_i+1] == ';')) { |
|
256 |
/* W; is a special case */ |
|
257 |
++short_i; |
|
258 |
continue; |
|
259 |
} |
|
260 |
||
261 |
equivFound = 0; |
|
262 |
if (long_options != NULL) { |
|
263 |
for (long_i = 0; ((!equivFound) && |
|
264 |
(long_options[long_i].name != NULL)); |
|
265 |
++long_i) { |
|
266 |
equivFound = (ch == long_options[long_i].val); |
|
267 |
} |
|
268 |
} |
|
269 |
if ((!equivFound) && (PRINT_ERROR)) { |
|
270 |
warnxchar(nargv[0], |
|
271 |
_libc_gettext( |
|
272 |
"%s: equivalent long option required -- %s"), |
|
273 |
ch); |
|
274 |
} |
|
275 |
} /* short_i */ |
|
276 |
||
277 |
/* |
|
278 |
* Find a short option for each long option. Note that if we came |
|
279 |
* out of the above loop with equivFound==0, we are already done. |
|
280 |
*/ |
|
281 |
if (equivFound && (long_options != NULL)) { |
|
282 |
for (long_i = 0; (equivFound && |
|
283 |
(long_options[long_i].name != NULL)); |
|
284 |
++long_i) { |
|
285 |
equivFound = ((long_options[long_i].val != 0) && |
|
286 |
(strchr(options, long_options[long_i].val) |
|
287 |
!= NULL)); |
|
288 |
||
289 |
if ((!equivFound) && (PRINT_ERROR)) { |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
290 |
warnx_getopt(nargv[0], |
0 | 291 |
_libc_gettext( |
292 |
"%s: equivalent short option required -- %s"), |
|
293 |
long_options[long_i].name); |
|
294 |
} |
|
295 |
} /* for long_i */ |
|
296 |
} |
|
297 |
||
298 |
return (equivFound? 0:-1); |
|
299 |
} /* verify_short_long_equivalents() */ |
|
300 |
||
301 |
/* |
|
302 |
* parse_long_options -- |
|
303 |
* Parse long options in argc/argv argument vector. |
|
304 |
* Returns -1 if short_too is set and the option does not match long_options. |
|
305 |
*/ |
|
306 |
static int |
|
307 |
parse_long_options(int nargc, char * const *nargv, const char *options, |
|
308 |
const struct option *long_options, int *idx, int short_too, |
|
309 |
uint_t flags) |
|
310 |
{ |
|
311 |
char *current_argv = NULL; |
|
312 |
char *argv_equal_ptr = NULL; |
|
313 |
size_t current_argv_len = 0; |
|
314 |
size_t long_option_len = 0; |
|
315 |
int i = 0; |
|
316 |
int match = 0; |
|
317 |
||
318 |
current_argv = place; |
|
319 |
match = -1; |
|
320 |
||
321 |
optind++; |
|
322 |
||
323 |
if ((argv_equal_ptr = strchr(current_argv, '=')) != NULL) { |
|
324 |
/* argument found (--option=arg) */ |
|
325 |
current_argv_len = (argv_equal_ptr - current_argv); |
|
326 |
argv_equal_ptr++; |
|
327 |
} else { |
|
328 |
current_argv_len = strlen(current_argv); |
|
329 |
} |
|
330 |
||
331 |
for (i = 0; (long_options[i].name != NULL); i++) { |
|
332 |
||
333 |
/* find matching long option */ |
|
334 |
if (strncmp(current_argv, long_options[i].name, |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
335 |
current_argv_len) != 0) { |
0 | 336 |
continue; /* no match */ |
337 |
} |
|
338 |
long_option_len = strlen(long_options[i].name); |
|
339 |
if ((!FLAG_IS_SET(FLAG_ABBREV)) && |
|
340 |
(long_option_len > current_argv_len)) { |
|
341 |
continue; /* Abbreviations are disabled */ |
|
342 |
} |
|
343 |
||
344 |
if (long_option_len == current_argv_len) { |
|
345 |
/* exact match */ |
|
346 |
match = i; |
|
347 |
break; |
|
348 |
} |
|
349 |
/* |
|
350 |
* If this is a known short option, don't allow |
|
351 |
* a partial match of a single character. |
|
352 |
*/ |
|
353 |
if (short_too && current_argv_len == 1) |
|
354 |
continue; |
|
355 |
||
356 |
if (match == -1) /* partial match */ |
|
357 |
match = i; |
|
358 |
else { |
|
359 |
/* ambiguous abbreviation */ |
|
360 |
if (PRINT_ERROR) { |
|
361 |
warnxlen(nargv[0], |
|
362 |
_libc_gettext( |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
363 |
"%s: ambiguous option -- %s"), |
0 | 364 |
(int)current_argv_len, |
365 |
current_argv); |
|
366 |
} |
|
367 |
optopt = 0; |
|
368 |
return (BADCH); |
|
369 |
} |
|
370 |
} /* for i */ |
|
371 |
if (match != -1) { /* option found */ |
|
372 |
if ((long_options[match].has_arg == no_argument) && |
|
373 |
(argv_equal_ptr != NULL)) { |
|
374 |
if (PRINT_ERROR) { |
|
375 |
warnxlen(nargv[0], |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
376 |
_libc_gettext( |
0 | 377 |
"%s: option doesn't take an argument -- %s"), |
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
378 |
(int)current_argv_len, |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
379 |
current_argv); |
0 | 380 |
} |
381 |
/* |
|
382 |
* XXX: GNU sets optopt to val regardless of flag |
|
383 |
*/ |
|
384 |
if (long_options[match].flag == NULL) |
|
385 |
optopt = long_options[match].val; |
|
386 |
else |
|
387 |
optopt = 0; |
|
388 |
return (BADARG); |
|
389 |
} |
|
390 |
if (long_options[match].has_arg == required_argument || |
|
391 |
long_options[match].has_arg == optional_argument) { |
|
392 |
if (argv_equal_ptr != NULL) { |
|
393 |
optarg = argv_equal_ptr; |
|
394 |
} else if (LONGOPT_REQUIRES_ARG(long_options[match])) { |
|
395 |
/* The next argv must be the option argument */ |
|
396 |
if (optind < nargc) { |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
397 |
optarg = nargv[optind]; |
0 | 398 |
} |
399 |
++optind; /* code below depends on this */ |
|
400 |
} |
|
401 |
} |
|
402 |
if (LONGOPT_REQUIRES_ARG(long_options[match]) && |
|
403 |
(optarg == NULL)) { |
|
404 |
/* |
|
405 |
* Missing argument; leading ':' indicates no error |
|
406 |
* should be generated. |
|
407 |
*/ |
|
408 |
if (PRINT_ERROR) { |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
409 |
warnx_getopt(nargv[0], |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
410 |
_libc_gettext( |
0 | 411 |
"%s: option requires an argument -- %s"), |
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
412 |
current_argv); |
0 | 413 |
} |
414 |
/* |
|
415 |
* XXX: GNU sets optopt to val regardless of flag |
|
416 |
*/ |
|
417 |
if (long_options[match].flag == NULL) |
|
418 |
optopt = long_options[match].val; |
|
419 |
else |
|
420 |
optopt = 0; |
|
421 |
--optind; |
|
422 |
return (BADARG); |
|
423 |
} |
|
424 |
} else { /* unknown option */ |
|
425 |
if (short_too) { |
|
426 |
--optind; |
|
427 |
return (-1); |
|
428 |
} |
|
429 |
if (PRINT_ERROR) { |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
430 |
warnx_getopt(nargv[0], |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
431 |
_libc_gettext("%s: illegal option -- %s"), |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
432 |
current_argv); |
0 | 433 |
} |
434 |
optopt = 0; |
|
435 |
return (BADCH); |
|
436 |
} |
|
437 |
if (idx) |
|
438 |
*idx = match; |
|
439 |
if (long_options[match].flag != NULL) { |
|
440 |
*long_options[match].flag = long_options[match].val; |
|
441 |
return (0); |
|
442 |
} else { |
|
443 |
optopt = long_options[match].val; |
|
444 |
return (optopt); |
|
445 |
} |
|
446 |
} /* parse_long_options() */ |
|
447 |
||
448 |
/* |
|
449 |
* getopt_internal() -- |
|
450 |
* Parse argc/argv argument vector. Called by user level routines. |
|
451 |
* |
|
452 |
* This implements all of the getopt_long(), getopt_long_only(), |
|
453 |
* getopt_clip() variants. |
|
454 |
*/ |
|
455 |
static int |
|
456 |
getopt_internal(int nargc, char * const *nargv, const char *options, |
|
457 |
const struct option *long_options, int *idx, uint_t flags) |
|
458 |
{ |
|
459 |
char *oli; /* option letter list index */ |
|
460 |
int optchar, short_too; |
|
461 |
static int posixly_correct = -1; |
|
462 |
||
463 |
if (options == NULL) |
|
464 |
return (-1); |
|
465 |
||
466 |
/* |
|
467 |
* Disable GNU extensions if POSIXLY_CORRECT is set or options |
|
468 |
* string begins with a '+'. |
|
469 |
*/ |
|
470 |
if (posixly_correct == -1) { |
|
471 |
posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); |
|
472 |
} |
|
473 |
if (FLAG_IS_SET(FLAG_PLUS_DASH_START)) { |
|
474 |
/* |
|
475 |
* + or - at start of optstring takes precedence |
|
476 |
* over POSIXLY_CORRECT. |
|
477 |
*/ |
|
478 |
if (*options == '+') { |
|
479 |
/* |
|
480 |
* leading + means POSIX-compliant; first non-option |
|
481 |
* ends option list. Therefore, don't permute args. |
|
482 |
*/ |
|
483 |
posixly_correct = 1; |
|
484 |
} else if (*options == '-') { |
|
485 |
posixly_correct = 0; |
|
486 |
flags |= FLAG_ALLARGS; |
|
487 |
} |
|
488 |
if ((*options == '+') || (*options == '-')) { |
|
489 |
options++; |
|
490 |
} |
|
491 |
} /* if FLAG_PLUS_DASH_START */ |
|
492 |
||
493 |
if (posixly_correct) { |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
494 |
flags &= ~FLAG_PERMUTE; |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
495 |
flags &= ~FLAG_ALLARGS; |
0 | 496 |
} |
497 |
||
498 |
/* |
|
499 |
* Some programs (like GNU cvs) set optind to 0 to restart |
|
500 |
* option processing. Work around this braindamage. |
|
501 |
* |
|
502 |
* The above problem comes from using global variables. We |
|
503 |
* should avoid their use in the future. |
|
504 |
*/ |
|
505 |
if (optind == 0) { |
|
506 |
optind = optreset = 1; |
|
507 |
} |
|
508 |
||
509 |
optarg = NULL; |
|
510 |
optopt = 0; |
|
511 |
||
512 |
if (optreset) { |
|
513 |
nonopt_start = nonopt_end = -1; |
|
514 |
} |
|
515 |
||
516 |
/* |
|
517 |
* On the first call, make sure that there is a short equivalent |
|
518 |
* for each long option, and vice versa. This is required by |
|
519 |
* Sun's CLIP specification (11/12/02). |
|
520 |
*/ |
|
521 |
if ((optind == 1) && FLAG_IS_SET(FLAG_REQUIRE_EQUIVALENTS)) { |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
522 |
if (verify_short_long_equivalents( |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
523 |
nargc, nargv, options, long_options, flags) < 0) { |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
524 |
/* function printed any necessary messages */ |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
525 |
errno = EINVAL; /* invalid argument */ |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
526 |
return (-1); |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
527 |
} |
0 | 528 |
} |
529 |
||
530 |
start: |
|
531 |
if (optreset || !*place) { /* update scanning pointer */ |
|
532 |
optreset = 0; |
|
533 |
if (optind >= nargc) { /* end of argument vector */ |
|
534 |
place = EMSG; |
|
535 |
if (nonopt_end != -1) { |
|
536 |
/* do permutation, if we have to */ |
|
537 |
permute_args(nonopt_start, nonopt_end, |
|
538 |
optind, nargv); |
|
539 |
optind -= nonopt_end - nonopt_start; |
|
540 |
||
541 |
} else if (nonopt_start != -1) { |
|
542 |
/* |
|
543 |
* If we skipped non-options, set optind |
|
544 |
* to the first of them. |
|
545 |
*/ |
|
546 |
optind = nonopt_start; |
|
547 |
} |
|
548 |
nonopt_start = nonopt_end = -1; |
|
549 |
return (-1); |
|
550 |
} |
|
551 |
if ((*(place = nargv[optind]) != '-') || (place[1] == '\0')) { |
|
552 |
place = EMSG; /* found non-option */ |
|
553 |
if (flags & FLAG_ALLARGS) { |
|
554 |
/* |
|
555 |
* GNU extension: |
|
556 |
* return non-option as argument to option '\1' |
|
557 |
*/ |
|
558 |
optarg = nargv[optind++]; |
|
559 |
return (INORDER); |
|
560 |
} |
|
561 |
if (!(flags & FLAG_PERMUTE)) { |
|
562 |
/* |
|
563 |
* If no permutation wanted, stop parsing |
|
564 |
* at first non-option. |
|
565 |
*/ |
|
566 |
return (-1); |
|
567 |
} |
|
568 |
/* do permutation */ |
|
569 |
if (nonopt_start == -1) |
|
570 |
nonopt_start = optind; |
|
571 |
else if (nonopt_end != -1) { |
|
572 |
permute_args(nonopt_start, nonopt_end, |
|
573 |
optind, nargv); |
|
574 |
nonopt_start = optind - |
|
575 |
(nonopt_end - nonopt_start); |
|
576 |
nonopt_end = -1; |
|
577 |
} |
|
578 |
optind++; |
|
579 |
/* process next argument */ |
|
580 |
goto start; |
|
581 |
} |
|
582 |
if (nonopt_start != -1 && nonopt_end == -1) |
|
583 |
nonopt_end = optind; |
|
584 |
||
585 |
/* |
|
586 |
* Check for "--" or "--foo" with no long options |
|
587 |
* but if place is simply "-" leave it unmolested. |
|
588 |
*/ |
|
589 |
if (place[1] != '\0' && *++place == '-' && |
|
590 |
(place[1] == '\0' || long_options == NULL)) { |
|
591 |
optind++; |
|
592 |
place = EMSG; |
|
593 |
/* |
|
594 |
* We found an option (--), so if we skipped |
|
595 |
* non-options, we have to permute. |
|
596 |
*/ |
|
597 |
if (nonopt_end != -1) { |
|
598 |
permute_args(nonopt_start, nonopt_end, |
|
599 |
optind, nargv); |
|
600 |
optind -= nonopt_end - nonopt_start; |
|
601 |
} |
|
602 |
nonopt_start = nonopt_end = -1; |
|
603 |
return (-1); |
|
604 |
} |
|
605 |
} |
|
606 |
||
607 |
/* |
|
608 |
* Check long options if: |
|
609 |
* 1) we were passed some |
|
610 |
* 2) the arg is not just "-" |
|
611 |
* 3) either the arg starts with -- or we are getopt_long_only() |
|
612 |
*/ |
|
613 |
if (long_options != NULL && place != nargv[optind] && |
|
614 |
(*place == '-' || (FLAG_IS_SET(FLAG_LONGONLY)))) { |
|
615 |
short_too = 0; |
|
616 |
if (*place == '-') |
|
617 |
place++; /* --foo long option */ |
|
618 |
else if (*place != ':' && strchr(options, *place) != NULL) |
|
619 |
short_too = 1; /* could be short option too */ |
|
620 |
||
621 |
optchar = parse_long_options(nargc, nargv, options, |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
622 |
long_options, idx, short_too, flags); |
0 | 623 |
if (optchar != -1) { |
624 |
place = EMSG; |
|
625 |
return (optchar); |
|
626 |
} |
|
627 |
} |
|
628 |
||
629 |
if ((optchar = (int)*place++) == (int)':' || |
|
630 |
(oli = strchr(options, optchar)) == NULL) { |
|
631 |
/* |
|
632 |
* If the user didn't specify '-' as an option, |
|
633 |
* assume it means -1 as POSIX specifies. |
|
634 |
*/ |
|
635 |
if (optchar == (int)'-') |
|
636 |
return (-1); |
|
637 |
/* option letter unknown or ':' */ |
|
638 |
if (!*place) |
|
639 |
++optind; |
|
640 |
if (PRINT_ERROR) |
|
641 |
warnxchar(nargv[0], |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
642 |
_libc_gettext("%s: illegal option -- %s"), |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
643 |
optchar); |
0 | 644 |
optopt = optchar; |
645 |
return (BADCH); |
|
646 |
} |
|
647 |
if (FLAG_IS_SET(FLAG_W_SEMICOLON) && |
|
648 |
(long_options != NULL) && (optchar == 'W') && (oli[1] == ';')) { |
|
649 |
/* -W long-option */ |
|
650 |
/* LINTED: statement has no consequent: if */ |
|
651 |
if (*place) { /* no space */ |
|
652 |
/* NOTHING */; |
|
653 |
} else if (++optind >= nargc) { /* no long-option after -W */ |
|
654 |
place = EMSG; |
|
655 |
if (PRINT_ERROR) |
|
656 |
warnxchar(nargv[0], |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
657 |
_libc_gettext( |
0 | 658 |
"%s: option requires an argument -- %s"), |
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
659 |
optchar); |
0 | 660 |
optopt = optchar; |
661 |
return (BADARG); |
|
662 |
} else { /* white space */ |
|
663 |
place = nargv[optind]; |
|
664 |
} |
|
665 |
optchar = parse_long_options( |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
666 |
nargc, nargv, options, long_options, |
0 | 667 |
idx, 0, flags); |
668 |
||
669 |
/* |
|
670 |
* PSARC 2003/645 - Match GNU behavior, set optarg to |
|
671 |
* the long-option. |
|
672 |
*/ |
|
673 |
if (optarg == NULL) { |
|
674 |
optarg = nargv[optind-1]; |
|
675 |
} |
|
676 |
place = EMSG; |
|
677 |
return (optchar); |
|
678 |
} |
|
679 |
if (*++oli != ':') { /* doesn't take argument */ |
|
680 |
if (!*place) |
|
681 |
++optind; |
|
682 |
} else { /* takes (optional) argument */ |
|
683 |
optarg = NULL; |
|
684 |
if (*place) { /* no white space */ |
|
685 |
optarg = place; |
|
686 |
/* XXX: disable test for :: if PC? (GNU doesn't) */ |
|
687 |
} else if (!(FLAG_IS_SET(FLAG_OPTIONAL_ARGS) && |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
688 |
(oli[1] == ':'))) { |
0 | 689 |
/* arg is required (not optional) */ |
690 |
||
691 |
if (++optind >= nargc) { /* no arg */ |
|
692 |
place = EMSG; |
|
693 |
if (PRINT_ERROR) { |
|
694 |
warnxchar(nargv[0], |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
695 |
_libc_gettext( |
0 | 696 |
"%s: option requires an argument -- %s"), |
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
697 |
optchar); |
0 | 698 |
} |
699 |
optopt = optchar; |
|
700 |
return (BADARG); |
|
701 |
} else |
|
702 |
optarg = nargv[optind]; |
|
703 |
} |
|
704 |
place = EMSG; |
|
705 |
++optind; |
|
706 |
} |
|
707 |
/* return valid option letter */ |
|
708 |
optopt = optchar; /* preserve getopt() behavior */ |
|
709 |
return (optchar); |
|
710 |
} /* getopt_internal() */ |
|
711 |
||
712 |
/* |
|
713 |
* getopt_long() -- |
|
714 |
* Parse argc/argv argument vector. |
|
715 |
* |
|
716 |
* Requires that long options be preceded with a two dashes |
|
717 |
* (e.g., --longoption). |
|
718 |
*/ |
|
719 |
int |
|
720 |
getopt_long(int nargc, char *const *nargv, |
|
721 |
const char *optstring, |
|
722 |
const struct option *long_options, int *long_index) |
|
723 |
{ |
|
724 |
||
725 |
return (getopt_internal( |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
726 |
nargc, nargv, optstring, long_options, long_index, |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
727 |
FLAG_PERMUTE |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
728 |
| FLAG_OPTIONAL_ARGS |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
729 |
| FLAG_ABBREV |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
730 |
| FLAG_W_SEMICOLON |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
731 |
| FLAG_PLUS_DASH_START)); |
0 | 732 |
} /* getopt_long() */ |
733 |
||
734 |
/* |
|
735 |
* getopt_long_only() -- |
|
736 |
* Parse argc/argv argument vector. |
|
737 |
* |
|
738 |
* Long options may be preceded with a single dash (e.g., -longoption) |
|
739 |
*/ |
|
740 |
int |
|
741 |
getopt_long_only(int nargc, char *const *nargv, |
|
742 |
const char *optstring, |
|
743 |
const struct option *long_options, int *long_index) |
|
744 |
{ |
|
745 |
||
746 |
return (getopt_internal( |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
747 |
nargc, nargv, optstring, long_options, long_index, |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
748 |
FLAG_PERMUTE |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
749 |
| FLAG_OPTIONAL_ARGS |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
750 |
| FLAG_ABBREV |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
751 |
| FLAG_W_SEMICOLON |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
752 |
| FLAG_PLUS_DASH_START |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
753 |
| FLAG_LONGONLY)); |
0 | 754 |
} /* getopt_long_only() */ |
755 |
||
756 |
/* |
|
757 |
* getopt_clip() -- |
|
758 |
* Parse argc/argv argument vector, requiring compliance with |
|
759 |
* Sun's CLIP specification (11/12/02) |
|
760 |
* |
|
761 |
* o Does not allow arguments to be optional (optional_argument is |
|
762 |
* treated as required_argument). |
|
763 |
* |
|
764 |
* o Does not allow long options to be abbreviated on the command line |
|
765 |
* |
|
766 |
* o Does not allow long argument to be preceded by a single dash |
|
767 |
* (Double-dash '--' is required) |
|
768 |
* |
|
769 |
* o Stops option processing at the first non-option |
|
770 |
* |
|
771 |
* o Requires that every long option have a short-option (single |
|
772 |
* character) equivalent and vice-versa. If a short option or |
|
773 |
* long option without an equivalent is found, an error message |
|
774 |
* is printed and -1 is returned on the first call, and errno |
|
775 |
* is set to EINVAL. |
|
776 |
* |
|
777 |
* o Leading + or - in optstring is ignored, and opstring is |
|
778 |
* treated as if it began after the + or - . |
|
779 |
*/ |
|
780 |
int |
|
781 |
getopt_clip(int nargc, char *const *nargv, |
|
782 |
const char *optstring, |
|
783 |
const struct option *long_options, int *long_index) |
|
784 |
{ |
|
785 |
return getopt_internal( |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
786 |
nargc, nargv, optstring, long_options, long_index, |
0 | 787 |
/* |
788 |
* no permutation, |
|
789 |
* no optional args, |
|
790 |
* no long-only, |
|
791 |
* no abbreviations |
|
792 |
* no support for +- at start of optstring |
|
793 |
* yes support for "W;" in optstring |
|
794 |
*/ |
|
4891
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
795 |
FLAG_W_SEMICOLON |
f4f971e9574d
PSARC/2006/662 Make err/warn part of Solaris's libc
vk199839
parents:
420
diff
changeset
|
796 |
| FLAG_REQUIRE_EQUIVALENTS); |
0 | 797 |
} /* getopt_clip() */ |