]>
Commit | Line | Data |
---|---|---|
2b6fc908 | 1 | /* |
735a6603 | 2 | * getopt.c - Enhanced implementation of BSD getopt(1) |
b5f3078e | 3 | * Copyright (c) 1997-2014 Frodo Looijaard <frodo@frodo.looijaard.name> |
735a6603 SK |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
7cebf0bb SK |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program; if not, write to the Free Software Foundation, Inc., | |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
735a6603 | 18 | */ |
2b6fc908 | 19 | |
f9a20f0b | 20 | /* |
2b6fc908 | 21 | * Version 1.0-b4: Tue Sep 23 1997. First public release. |
f9a20f0b | 22 | * Version 1.0: Wed Nov 19 1997. |
2b6fc908 KZ |
23 | * Bumped up the version number to 1.0 |
24 | * Fixed minor typo (CSH instead of TCSH) | |
25 | * Version 1.0.1: Tue Jun 3 1998 | |
26 | * Fixed sizeof instead of strlen bug | |
27 | * Bumped up the version number to 1.0.1 | |
28 | * Version 1.0.2: Thu Jun 11 1998 (not present) | |
29 | * Fixed gcc-2.8.1 warnings | |
30 | * Fixed --version/-V option (not present) | |
66ee8158 KZ |
31 | * Version 1.0.5: Tue Jun 22 1999 |
32 | * Make -u option work (not present) | |
33 | * Version 1.0.6: Tue Jun 27 2000 | |
34 | * No important changes | |
35 | * Version 1.1.0: Tue Jun 30 2000 | |
b50945d4 | 36 | * Added NLS support (partly written by Arkadiusz MiĆkiewicz |
df1dddf9 | 37 | * <misiek@pld.org.pl>) |
cf6d7fae KZ |
38 | * Version 1.1.4: Mon Nov 7 2005 |
39 | * Fixed a few type's in the manpage | |
b5f3078e SK |
40 | * Version 1.1.5: Sun Aug 12 2012 |
41 | * Sync with util-linux-2.21, fixed build problems, many new translations | |
42 | * Version 1.1.6: Mon Nov 24 2014 | |
43 | * Sync with util-linux git 20141120, detect ambiguous long options, fix | |
44 | * backslash problem in tcsh | |
2b6fc908 KZ |
45 | */ |
46 | ||
d1d03b54 | 47 | /* Exit codes: |
455fe9a0 | 48 | * 0) No errors, successful operation. |
d1d03b54 SK |
49 | * 1) getopt(3) returned an error. |
50 | * 2) A problem with parameter parsing for getopt(1). | |
51 | * 3) Internal error, out of memory | |
52 | * 4) Returned for -T | |
53 | */ | |
54 | #define GETOPT_EXIT_CODE 1 | |
55 | #define PARAMETER_EXIT_CODE 2 | |
56 | #define XALLOC_EXIT_CODE 3 | |
090d8c76 | 57 | #define CLOSE_EXIT_CODE XALLOC_EXIT_CODE |
d1d03b54 SK |
58 | #define TEST_EXIT_CODE 4 |
59 | ||
2b6fc908 KZ |
60 | #include <stdio.h> |
61 | #include <stdlib.h> | |
62 | #include <string.h> | |
63 | #include <unistd.h> | |
64 | #include <ctype.h> | |
2b6fc908 | 65 | #include <getopt.h> |
5c78d493 RM |
66 | #ifdef HAVE_SYS_PARAM_H |
67 | # include <sys/param.h> /* BSD */ | |
68 | #endif | |
2b6fc908 | 69 | |
3862f685 | 70 | #include "closestream.h" |
df1dddf9 | 71 | #include "nls.h" |
d1d03b54 | 72 | #include "xalloc.h" |
66ee8158 | 73 | |
d01f47f6 SK |
74 | /* NON_OPT is the code that is returned getopt(3) when a non-option is |
75 | * found in 'char optstring[]="-abc...";', e.g., it begins by '-' */ | |
2b6fc908 KZ |
76 | #define NON_OPT 1 |
77 | /* LONG_OPT is the code that is returned when a long option is found. */ | |
b5f3078e | 78 | #define LONG_OPT 0 |
2b6fc908 KZ |
79 | |
80 | /* The shells recognized. */ | |
735a6603 | 81 | typedef enum { BASH, TCSH } shell_t; |
2b6fc908 | 82 | |
1dacc901 SK |
83 | struct getopt_control { |
84 | shell_t shell; /* the shell we generate output for */ | |
85 | char *optstr; /* getopt(3) optstring */ | |
e402d6d3 | 86 | char *name; |
1dacc901 SK |
87 | struct option *long_options; /* long options */ |
88 | int long_options_length; /* length of options array */ | |
89 | int long_options_nr; /* number of used elements in array */ | |
90 | unsigned int | |
91 | compatible:1, /* compatibility mode for 'difficult' programs */ | |
92 | quiet_errors:1, /* print errors */ | |
93 | quiet_output:1, /* print output */ | |
94 | quote:1; /* quote output */ | |
95 | }; | |
96 | ||
97 | enum { REALLOC_INCREMENT = 8 }; | |
4a898108 | 98 | |
ae769172 | 99 | /* Allow changing which getopt is in use with function pointer. */ |
2ba641e5 SK |
100 | static int (*getopt_long_fp) (int argc, char *const *argv, const char *optstr, |
101 | const struct option * longopts, int *longindex); | |
2b6fc908 | 102 | |
2b6fc908 | 103 | /* |
ae769172 | 104 | * This function 'normalizes' a single argument: it puts single quotes |
735a6603 SK |
105 | * around it and escapes other special characters. If quote is false, it |
106 | * just returns its argument. | |
107 | * | |
2b6fc908 | 108 | * Bash only needs special treatment for single quotes; tcsh also recognizes |
735a6603 SK |
109 | * exclamation marks within single quotes, and nukes whitespace. This |
110 | * function returns a pointer to a buffer that is overwritten by each call. | |
2b6fc908 | 111 | */ |
c29b090f | 112 | static void print_normalized(const struct getopt_control *ctl, const char *arg) |
2b6fc908 | 113 | { |
eba520d8 | 114 | char *buf; |
735a6603 | 115 | const char *argptr = arg; |
2b6fc908 KZ |
116 | char *bufptr; |
117 | ||
1dacc901 | 118 | if (!ctl->quote) { |
eba520d8 SK |
119 | printf(" %s", arg); |
120 | return; | |
2b6fc908 KZ |
121 | } |
122 | ||
735a6603 | 123 | /* |
455fe9a0 | 124 | * Each character in arg may take up to four characters in the |
735a6603 SK |
125 | * result: For a quote we need a closing quote, a backslash, a quote |
126 | * and an opening quote! We need also the global opening and closing | |
127 | * quote, and one extra character for '\0'. | |
128 | */ | |
eba520d8 | 129 | buf = xmalloc(strlen(arg) * 4 + 3); |
eba520d8 | 130 | bufptr = buf; |
2b6fc908 | 131 | |
bb742d73 SK |
132 | for (*bufptr++ = '\''; *argptr; argptr++) { |
133 | if (ctl->shell == TCSH) { | |
134 | switch (*argptr) { | |
135 | case '\\': | |
136 | /* Backslash: replace it with: '\\' */ | |
137 | *bufptr++ = '\\'; | |
138 | *bufptr++ = '\\'; | |
139 | continue; | |
140 | case '!': | |
141 | /* Exclamation mark: replace it with: \! */ | |
142 | *bufptr++ = '\''; | |
143 | *bufptr++ = '\\'; | |
144 | *bufptr++ = '!'; | |
145 | *bufptr++ = '\''; | |
146 | continue; | |
147 | case '\n': | |
148 | /* Newline: replace it with: \n */ | |
149 | *bufptr++ = '\\'; | |
150 | *bufptr++ = 'n'; | |
151 | continue; | |
152 | } | |
153 | if (isspace(*argptr)) { | |
154 | /* Non-newline whitespace: replace it with \<ws> */ | |
155 | *bufptr++ = '\''; | |
156 | *bufptr++ = '\\'; | |
157 | *bufptr++ = *argptr; | |
158 | *bufptr++ = '\''; | |
159 | continue; | |
160 | } | |
161 | } | |
2b6fc908 KZ |
162 | if (*argptr == '\'') { |
163 | /* Quote: replace it with: '\'' */ | |
735a6603 SK |
164 | *bufptr++ = '\''; |
165 | *bufptr++ = '\\'; | |
166 | *bufptr++ = '\''; | |
167 | *bufptr++ = '\''; | |
2b6fc908 KZ |
168 | } else |
169 | /* Just copy */ | |
735a6603 | 170 | *bufptr++ = *argptr; |
2b6fc908 | 171 | } |
bb742d73 | 172 | |
735a6603 SK |
173 | *bufptr++ = '\''; |
174 | *bufptr++ = '\0'; | |
eba520d8 SK |
175 | printf(" %s", buf); |
176 | free(buf); | |
2b6fc908 KZ |
177 | } |
178 | ||
f9a20f0b | 179 | /* |
2b6fc908 KZ |
180 | * Generate the output. argv[0] is the program name (used for reporting errors). |
181 | * argv[1..] contains the options to be parsed. argc must be the number of | |
182 | * elements in argv (ie. 1 if there are no options, only the program name), | |
183 | * optstr must contain the short options, and longopts the long options. | |
184 | * Other settings are found in global variables. | |
185 | */ | |
b13fedd4 | 186 | static int generate_output(struct getopt_control *ctl, char *argv[], int argc) |
2b6fc908 | 187 | { |
735a6603 | 188 | int exit_code = EXIT_SUCCESS; /* Assume everything will be OK */ |
2b6fc908 KZ |
189 | int opt; |
190 | int longindex; | |
191 | const char *charptr; | |
192 | ||
1dacc901 | 193 | if (ctl->quiet_errors) |
735a6603 SK |
194 | /* No error reporting from getopt(3) */ |
195 | opterr = 0; | |
196 | /* Reset getopt(3) */ | |
197 | optind = 0; | |
2b6fc908 | 198 | |
735a6603 | 199 | while ((opt = |
b13fedd4 SK |
200 | (getopt_long_fp |
201 | (argc, argv, ctl->optstr, | |
202 | (const struct option *)ctl->long_options, &longindex))) | |
203 | != EOF) { | |
735a6603 | 204 | if (opt == '?' || opt == ':') |
d1d03b54 | 205 | exit_code = GETOPT_EXIT_CODE; |
1dacc901 | 206 | else if (!ctl->quiet_output) { |
bb742d73 SK |
207 | switch (opt) { |
208 | case LONG_OPT: | |
1dacc901 SK |
209 | printf(" --%s", ctl->long_options[longindex].name); |
210 | if (ctl->long_options[longindex].has_arg) | |
c29b090f | 211 | print_normalized(ctl, optarg ? optarg : ""); |
bb742d73 SK |
212 | break; |
213 | case NON_OPT: | |
c29b090f | 214 | print_normalized(ctl, optarg ? optarg : ""); |
bb742d73 SK |
215 | break; |
216 | default: | |
735a6603 | 217 | printf(" -%c", opt); |
1dacc901 | 218 | charptr = strchr(ctl->optstr, opt); |
2b6fc908 | 219 | if (charptr != NULL && *++charptr == ':') |
c29b090f | 220 | print_normalized(ctl, optarg ? optarg : ""); |
2b6fc908 KZ |
221 | } |
222 | } | |
b13fedd4 | 223 | } |
1dacc901 | 224 | if (!ctl->quiet_output) { |
2b6fc908 | 225 | printf(" --"); |
735a6603 | 226 | while (optind < argc) |
c29b090f | 227 | print_normalized(ctl, argv[optind++]); |
2b6fc908 KZ |
228 | printf("\n"); |
229 | } | |
b13fedd4 SK |
230 | for (longindex = 0; longindex < ctl->long_options_nr; longindex++) |
231 | free((char *)ctl->long_options[longindex].name); | |
232 | free(ctl->long_options); | |
233 | free(ctl->optstr); | |
e402d6d3 | 234 | free(ctl->name); |
2b6fc908 KZ |
235 | return exit_code; |
236 | } | |
237 | ||
238 | /* | |
735a6603 SK |
239 | * Report an error when parsing getopt's own arguments. If message is NULL, |
240 | * we already sent a message, we just exit with a helpful hint. | |
2b6fc908 | 241 | */ |
735a6603 | 242 | static void __attribute__ ((__noreturn__)) parse_error(const char *message) |
2b6fc908 KZ |
243 | { |
244 | if (message) | |
735a6603 | 245 | warnx("%s", message); |
6d7bee26 | 246 | errtryhelp(PARAMETER_EXIT_CODE); |
2b6fc908 KZ |
247 | } |
248 | ||
2b6fc908 KZ |
249 | |
250 | /* Register a long option. The contents of name is copied. */ | |
1dacc901 | 251 | static void add_longopt(struct getopt_control *ctl, const char *name, int has_arg) |
2b6fc908 | 252 | { |
b5f3078e | 253 | static int flag; |
e1164591 | 254 | int nr = ctl->long_options_nr; |
b5f3078e | 255 | |
1dacc901 SK |
256 | if (ctl->long_options_nr == ctl->long_options_length) { |
257 | ctl->long_options_length += REALLOC_INCREMENT; | |
258 | ctl->long_options = xrealloc(ctl->long_options, | |
259 | sizeof(struct option) * | |
260 | ctl->long_options_length); | |
2b6fc908 | 261 | } |
0640689c | 262 | if (name) { |
735a6603 | 263 | /* Not for init! */ |
e1164591 KZ |
264 | ctl->long_options[nr].has_arg = has_arg; |
265 | ctl->long_options[nr].flag = &flag; | |
266 | ctl->long_options[nr].val = ctl->long_options_nr; | |
267 | ctl->long_options[nr].name = xstrdup(name); | |
268 | } else { | |
269 | /* lets use add_longopt(ct, NULL, 0) to terminate the array */ | |
270 | ctl->long_options[nr].name = NULL; | |
271 | ctl->long_options[nr].has_arg = 0; | |
272 | ctl->long_options[nr].flag = NULL; | |
273 | ctl->long_options[nr].val = 0; | |
2b6fc908 | 274 | } |
2b6fc908 | 275 | } |
735a6603 | 276 | |
2b6fc908 | 277 | |
f9a20f0b | 278 | /* |
735a6603 SK |
279 | * Register several long options. options is a string of long options, |
280 | * separated by commas or whitespace. This nukes options! | |
2b6fc908 | 281 | */ |
1dacc901 | 282 | static void add_long_options(struct getopt_control *ctl, char *options) |
2b6fc908 KZ |
283 | { |
284 | int arg_opt; | |
735a6603 | 285 | char *tokptr = strtok(options, ", \t\n"); |
4b4e1b10 | 286 | |
2b6fc908 | 287 | while (tokptr) { |
4b4e1b10 KZ |
288 | size_t len = strlen(tokptr); |
289 | ||
735a6603 | 290 | arg_opt = no_argument; |
4b4e1b10 KZ |
291 | if (len > 0) { |
292 | if (tokptr[len - 1] == ':') { | |
293 | if (tokptr[len - 2] == ':') { | |
294 | tokptr[len - 2] = '\0'; | |
735a6603 | 295 | arg_opt = optional_argument; |
2b6fc908 | 296 | } else { |
4b4e1b10 | 297 | tokptr[len - 1] = '\0'; |
735a6603 | 298 | arg_opt = required_argument; |
2b6fc908 | 299 | } |
4b4e1b10 | 300 | if (!*tokptr) |
735a6603 SK |
301 | parse_error(_ |
302 | ("empty long option after " | |
303 | "-l or --long argument")); | |
2b6fc908 | 304 | } |
1dacc901 | 305 | add_longopt(ctl, tokptr, arg_opt); |
3aa14187 | 306 | ctl->long_options_nr++; |
2b6fc908 | 307 | } |
735a6603 | 308 | tokptr = strtok(NULL, ", \t\n"); |
2b6fc908 | 309 | } |
f7b38b87 | 310 | add_longopt(ctl, NULL, 0); /* ensure long_options[] is not full */ |
2b6fc908 KZ |
311 | } |
312 | ||
80dd38e9 | 313 | static shell_t shell_type(const char *new_shell) |
2b6fc908 | 314 | { |
735a6603 | 315 | if (!strcmp(new_shell, "bash")) |
80dd38e9 SK |
316 | return BASH; |
317 | if (!strcmp(new_shell, "sh")) | |
318 | return BASH; | |
319 | if (!strcmp(new_shell, "tcsh")) | |
320 | return TCSH; | |
321 | if (!strcmp(new_shell, "csh")) | |
322 | return TCSH; | |
323 | parse_error(_("unknown shell after -s or --shell argument")); | |
2b6fc908 KZ |
324 | } |
325 | ||
6d7bee26 | 326 | static void __attribute__((__noreturn__)) usage(void) |
2b6fc908 | 327 | { |
6d7bee26 RM |
328 | fputs(USAGE_HEADER, stdout); |
329 | printf(_( | |
ae769172 BS |
330 | " %1$s <optstring> <parameters>\n" |
331 | " %1$s [options] [--] <optstring> <parameters>\n" | |
332 | " %1$s [options] -o|--options <optstring> [options] [--] <parameters>\n"), | |
283f8f02 | 333 | program_invocation_short_name); |
ca38f611 | 334 | |
6d7bee26 RM |
335 | fputs(USAGE_SEPARATOR, stdout); |
336 | fputs(_("Parse command options.\n"), stdout); | |
337 | ||
338 | fputs(USAGE_OPTIONS, stdout); | |
339 | fputs(_(" -a, --alternative allow long options starting with single -\n"), stdout); | |
340 | fputs(_(" -l, --longoptions <longopts> the long options to be recognized\n"), stdout); | |
341 | fputs(_(" -n, --name <progname> the name under which errors are reported\n"), stdout); | |
342 | fputs(_(" -o, --options <optstring> the short options to be recognized\n"), stdout); | |
343 | fputs(_(" -q, --quiet disable error reporting by getopt(3)\n"), stdout); | |
344 | fputs(_(" -Q, --quiet-output no normal output\n"), stdout); | |
345 | fputs(_(" -s, --shell <shell> set quoting conventions to those of <shell>\n"), stdout); | |
346 | fputs(_(" -T, --test test for getopt(1) version\n"), stdout); | |
347 | fputs(_(" -u, --unquoted do not quote the output\n"), stdout); | |
348 | fputs(USAGE_SEPARATOR, stdout); | |
f45f3ec3 | 349 | printf(USAGE_HELP_OPTIONS(31)); |
6d7bee26 RM |
350 | printf(USAGE_MAN_TAIL("getopt(1)")); |
351 | exit(EXIT_SUCCESS); | |
2b6fc908 | 352 | } |
2b6fc908 KZ |
353 | |
354 | int main(int argc, char *argv[]) | |
355 | { | |
1dacc901 SK |
356 | struct getopt_control ctl = { |
357 | .shell = BASH, | |
358 | .quote = 1 | |
359 | }; | |
5c36a0eb | 360 | int opt; |
2b6fc908 | 361 | |
283f8f02 SK |
362 | /* Stop scanning as soon as a non-option argument is found! */ |
363 | static const char *shortopts = "+ao:l:n:qQs:TuhV"; | |
364 | static const struct option longopts[] = { | |
365 | {"options", required_argument, NULL, 'o'}, | |
366 | {"longoptions", required_argument, NULL, 'l'}, | |
367 | {"quiet", no_argument, NULL, 'q'}, | |
368 | {"quiet-output", no_argument, NULL, 'Q'}, | |
369 | {"shell", required_argument, NULL, 's'}, | |
370 | {"test", no_argument, NULL, 'T'}, | |
371 | {"unquoted", no_argument, NULL, 'u'}, | |
372 | {"help", no_argument, NULL, 'h'}, | |
373 | {"alternative", no_argument, NULL, 'a'}, | |
374 | {"name", required_argument, NULL, 'n'}, | |
375 | {"version", no_argument, NULL, 'V'}, | |
376 | {NULL, 0, NULL, 0} | |
377 | }; | |
378 | ||
735a6603 | 379 | setlocale(LC_ALL, ""); |
7eda085c KZ |
380 | bindtextdomain(PACKAGE, LOCALEDIR); |
381 | textdomain(PACKAGE); | |
2c308875 | 382 | close_stdout_atexit(); |
7eda085c | 383 | |
735a6603 | 384 | if (getenv("GETOPT_COMPATIBLE")) |
1dacc901 | 385 | ctl.compatible = 1; |
2b6fc908 | 386 | |
735a6603 | 387 | if (argc == 1) { |
1dacc901 | 388 | if (ctl.compatible) { |
735a6603 SK |
389 | /* |
390 | * For some reason, the original getopt gave no | |
391 | * error when there were no arguments. | |
392 | */ | |
2b6fc908 | 393 | printf(" --\n"); |
d1d03b54 | 394 | return EXIT_SUCCESS; |
042f62df RP |
395 | } |
396 | parse_error(_("missing optstring argument")); | |
2b6fc908 | 397 | } |
735a6603 | 398 | |
b13fedd4 SK |
399 | add_longopt(&ctl, NULL, 0); /* init */ |
400 | getopt_long_fp = getopt_long; | |
401 | ||
1dacc901 SK |
402 | if (argv[1][0] != '-' || ctl.compatible) { |
403 | ctl.quote = 0; | |
404 | ctl.optstr = xmalloc(strlen(argv[1]) + 1); | |
405 | strcpy(ctl.optstr, argv[1] + strspn(argv[1], "-+")); | |
735a6603 | 406 | argv[1] = argv[0]; |
1dacc901 | 407 | return generate_output(&ctl, argv + 1, argc - 1); |
2b6fc908 | 408 | } |
735a6603 SK |
409 | |
410 | while ((opt = | |
411 | getopt_long(argc, argv, shortopts, longopts, NULL)) != EOF) | |
2b6fc908 KZ |
412 | switch (opt) { |
413 | case 'a': | |
4a898108 | 414 | getopt_long_fp = getopt_long_only; |
2b6fc908 | 415 | break; |
2b6fc908 | 416 | case 'o': |
1dacc901 | 417 | free(ctl.optstr); |
05ac43f5 | 418 | ctl.optstr = xstrdup(optarg); |
2b6fc908 KZ |
419 | break; |
420 | case 'l': | |
1dacc901 | 421 | add_long_options(&ctl, optarg); |
2b6fc908 KZ |
422 | break; |
423 | case 'n': | |
e402d6d3 KZ |
424 | free(ctl.name); |
425 | ctl.name = xstrdup(optarg); | |
2b6fc908 KZ |
426 | break; |
427 | case 'q': | |
1dacc901 | 428 | ctl.quiet_errors = 1; |
2b6fc908 KZ |
429 | break; |
430 | case 'Q': | |
1dacc901 | 431 | ctl.quiet_output = 1; |
2b6fc908 KZ |
432 | break; |
433 | case 's': | |
80dd38e9 | 434 | ctl.shell = shell_type(optarg); |
2b6fc908 KZ |
435 | break; |
436 | case 'T': | |
b13fedd4 | 437 | free(ctl.long_options); |
d1d03b54 | 438 | return TEST_EXIT_CODE; |
66ee8158 | 439 | case 'u': |
1dacc901 | 440 | ctl.quote = 0; |
66ee8158 | 441 | break; |
2c308875 | 442 | |
2b6fc908 | 443 | case 'V': |
2c308875 | 444 | print_version(EXIT_SUCCESS); |
2b6fc908 KZ |
445 | case '?': |
446 | case ':': | |
447 | parse_error(NULL); | |
2c308875 KZ |
448 | case 'h': |
449 | usage(); | |
2b6fc908 | 450 | default: |
7eda085c | 451 | parse_error(_("internal error, contact the author.")); |
2b6fc908 | 452 | } |
f9a20f0b | 453 | |
1dacc901 | 454 | if (!ctl.optstr) { |
2b6fc908 | 455 | if (optind >= argc) |
7eda085c | 456 | parse_error(_("missing optstring argument")); |
2b6fc908 | 457 | else { |
05ac43f5 | 458 | ctl.optstr = xstrdup(argv[optind]); |
2b6fc908 KZ |
459 | optind++; |
460 | } | |
461 | } | |
5c78d493 | 462 | |
e402d6d3 KZ |
463 | if (ctl.name) { |
464 | argv[optind - 1] = ctl.name; | |
30fbf2f6 | 465 | #if defined (HAVE_SETPROGNAME) && !defined (__linux__) |
e402d6d3 | 466 | setprogname(ctl.name); |
5c78d493 RM |
467 | #endif |
468 | } else | |
735a6603 SK |
469 | argv[optind - 1] = argv[0]; |
470 | ||
1dacc901 | 471 | return generate_output(&ctl, argv + optind - 1, argc - optind + 1); |
2b6fc908 | 472 | } |