Coverage Report

Created: 2025-07-02 06:56

/src/openssh/openbsd-compat/getopt_long.c
Line
Count
Source (jump to first uncovered line)
1
/*  $OpenBSD: getopt_long.c,v 1.25 2011/03/05 22:10:11 guenther Exp $ */
2
/*  $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $  */
3
4
/*
5
 * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
 *
19
 * Sponsored in part by the Defense Advanced Research Projects
20
 * Agency (DARPA) and Air Force Research Laboratory, Air Force
21
 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22
 */
23
/*-
24
 * Copyright (c) 2000 The NetBSD Foundation, Inc.
25
 * All rights reserved.
26
 *
27
 * This code is derived from software contributed to The NetBSD Foundation
28
 * by Dieter Baron and Thomas Klausner.
29
 *
30
 * Redistribution and use in source and binary forms, with or without
31
 * modification, are permitted provided that the following conditions
32
 * are met:
33
 * 1. Redistributions of source code must retain the above copyright
34
 *    notice, this list of conditions and the following disclaimer.
35
 * 2. Redistributions in binary form must reproduce the above copyright
36
 *    notice, this list of conditions and the following disclaimer in the
37
 *    documentation and/or other materials provided with the distribution.
38
 *
39
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
40
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
41
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
43
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
44
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
45
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
46
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
48
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
49
 * POSSIBILITY OF SUCH DAMAGE.
50
 */
51
52
/* OPENBSD ORIGINAL: lib/libc/stdlib/getopt_long.c */
53
#include "includes.h"
54
55
#if !defined(HAVE_GETOPT) || !defined(HAVE_GETOPT_OPTRESET)
56
57
/*
58
 * Some defines to make it easier to keep the code in sync with upstream.
59
 * getopt opterr optind optopt optreset optarg are all in defines.h which is
60
 * pulled in by includes.h.
61
 */
62
0
#define warnx   logit
63
64
#if 0
65
#include <err.h>
66
#include <getopt.h>
67
#endif
68
#include <errno.h>
69
#include <stdlib.h>
70
#include <string.h>
71
#include <stdarg.h>
72
73
#include "log.h"
74
75
struct option {
76
  /* name of long option */
77
  const char *name;
78
  /*
79
   * one of no_argument, required_argument, and optional_argument:
80
   * whether option takes an argument
81
   */
82
  int has_arg;
83
  /* if not NULL, set *flag to val when option found */
84
  int *flag;
85
  /* if flag not NULL, value to set *flag to; else return value */
86
  int val;
87
};
88
89
int opterr = 1;   /* if error message should be printed */
90
int optind = 1;   /* index into parent argv vector */
91
int optopt = '?';   /* character checked for validity */
92
int optreset;   /* reset getopt */
93
char    *optarg;    /* argument associated with option */
94
95
0
#define PRINT_ERROR ((opterr) && (*options != ':'))
96
97
0
#define FLAG_PERMUTE  0x01  /* permute non-options to the end of argv */
98
0
#define FLAG_ALLARGS  0x02  /* treat non-options as args to option "-1" */
99
0
#define FLAG_LONGONLY 0x04  /* operate as getopt_long_only */
100
101
/* return values */
102
0
#define BADCH   (int)'?'
103
0
#define BADARG    ((*options == ':') ? (int)':' : (int)'?')
104
0
#define INORDER   (int)1
105
106
0
#define EMSG    ""
107
108
static int getopt_internal(int, char * const *, const char *,
109
         const struct option *, int *, int);
110
static int parse_long_options(char * const *, const char *,
111
            const struct option *, int *, int);
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
/* Error messages */
122
static const char recargchar[] = "option requires an argument -- %c";
123
static const char recargstring[] = "option requires an argument -- %s";
124
static const char ambig[] = "ambiguous option -- %.*s";
125
static const char noarg[] = "option doesn't take an argument -- %.*s";
126
static const char illoptchar[] = "unknown option -- %c";
127
static const char illoptstring[] = "unknown option -- %s";
128
129
/*
130
 * Compute the greatest common divisor of a and b.
131
 */
132
static int
133
gcd(int a, int b)
134
0
{
135
0
  int c;
136
137
0
  c = a % b;
138
0
  while (c != 0) {
139
0
    a = b;
140
0
    b = c;
141
0
    c = a % b;
142
0
  }
143
144
0
  return (b);
145
0
}
146
147
/*
148
 * Exchange the block from nonopt_start to nonopt_end with the block
149
 * from nonopt_end to opt_end (keeping the same order of arguments
150
 * in each block).
151
 */
152
static void
153
permute_args(int panonopt_start, int panonopt_end, int opt_end,
154
  char * const *nargv)
155
0
{
156
0
  int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
157
0
  char *swap;
158
159
  /*
160
   * compute lengths of blocks and number and size of cycles
161
   */
162
0
  nnonopts = panonopt_end - panonopt_start;
163
0
  nopts = opt_end - panonopt_end;
164
0
  ncycle = gcd(nnonopts, nopts);
165
0
  cyclelen = (opt_end - panonopt_start) / ncycle;
166
167
0
  for (i = 0; i < ncycle; i++) {
168
0
    cstart = panonopt_end+i;
169
0
    pos = cstart;
170
0
    for (j = 0; j < cyclelen; j++) {
171
0
      if (pos >= panonopt_end)
172
0
        pos -= nnonopts;
173
0
      else
174
0
        pos += nopts;
175
0
      swap = nargv[pos];
176
      /* LINTED const cast */
177
0
      ((char **) nargv)[pos] = nargv[cstart];
178
      /* LINTED const cast */
179
0
      ((char **)nargv)[cstart] = swap;
180
0
    }
181
0
  }
182
0
}
183
184
/*
185
 * parse_long_options --
186
 *  Parse long options in argc/argv argument vector.
187
 * Returns -1 if short_too is set and the option does not match long_options.
188
 */
189
static int
190
parse_long_options(char * const *nargv, const char *options,
191
  const struct option *long_options, int *idx, int short_too)
192
0
{
193
0
  char *current_argv, *has_equal;
194
0
  size_t current_argv_len;
195
0
  int i, match;
196
197
0
  current_argv = place;
198
0
  match = -1;
199
200
0
  optind++;
201
202
0
  if ((has_equal = strchr(current_argv, '=')) != NULL) {
203
    /* argument found (--option=arg) */
204
0
    current_argv_len = has_equal - current_argv;
205
0
    has_equal++;
206
0
  } else
207
0
    current_argv_len = strlen(current_argv);
208
209
0
  for (i = 0; long_options[i].name; i++) {
210
    /* find matching long option */
211
0
    if (strncmp(current_argv, long_options[i].name,
212
0
        current_argv_len))
213
0
      continue;
214
215
0
    if (strlen(long_options[i].name) == current_argv_len) {
216
      /* exact match */
217
0
      match = i;
218
0
      break;
219
0
    }
220
    /*
221
     * If this is a known short option, don't allow
222
     * a partial match of a single character.
223
     */
224
0
    if (short_too && current_argv_len == 1)
225
0
      continue;
226
227
0
    if (match == -1) /* partial match */
228
0
      match = i;
229
0
    else {
230
      /* ambiguous abbreviation */
231
0
      if (PRINT_ERROR)
232
0
        warnx(ambig, (int)current_argv_len,
233
0
             current_argv);
234
0
      optopt = 0;
235
0
      return (BADCH);
236
0
    }
237
0
  }
238
0
  if (match != -1) {   /* option found */
239
0
    if (long_options[match].has_arg == no_argument
240
0
        && has_equal) {
241
0
      if (PRINT_ERROR)
242
0
        warnx(noarg, (int)current_argv_len,
243
0
             current_argv);
244
      /*
245
       * XXX: GNU sets optopt to val regardless of flag
246
       */
247
0
      if (long_options[match].flag == NULL)
248
0
        optopt = long_options[match].val;
249
0
      else
250
0
        optopt = 0;
251
0
      return (BADARG);
252
0
    }
253
0
    if (long_options[match].has_arg == required_argument ||
254
0
        long_options[match].has_arg == optional_argument) {
255
0
      if (has_equal)
256
0
        optarg = has_equal;
257
0
      else if (long_options[match].has_arg ==
258
0
          required_argument) {
259
        /*
260
         * optional argument doesn't use next nargv
261
         */
262
0
        optarg = nargv[optind++];
263
0
      }
264
0
    }
265
0
    if ((long_options[match].has_arg == required_argument)
266
0
        && (optarg == NULL)) {
267
      /*
268
       * Missing argument; leading ':' indicates no error
269
       * should be generated.
270
       */
271
0
      if (PRINT_ERROR)
272
0
        warnx(recargstring,
273
0
            current_argv);
274
      /*
275
       * XXX: GNU sets optopt to val regardless of flag
276
       */
277
0
      if (long_options[match].flag == NULL)
278
0
        optopt = long_options[match].val;
279
0
      else
280
0
        optopt = 0;
281
0
      --optind;
282
0
      return (BADARG);
283
0
    }
284
0
  } else {     /* unknown option */
285
0
    if (short_too) {
286
0
      --optind;
287
0
      return (-1);
288
0
    }
289
0
    if (PRINT_ERROR)
290
0
      warnx(illoptstring, current_argv);
291
0
    optopt = 0;
292
0
    return (BADCH);
293
0
  }
294
0
  if (idx)
295
0
    *idx = match;
296
0
  if (long_options[match].flag) {
297
0
    *long_options[match].flag = long_options[match].val;
298
0
    return (0);
299
0
  } else
300
0
    return (long_options[match].val);
301
0
}
302
303
/*
304
 * getopt_internal --
305
 *  Parse argc/argv argument vector.  Called by user level routines.
306
 */
307
static int
308
getopt_internal(int nargc, char * const *nargv, const char *options,
309
  const struct option *long_options, int *idx, int flags)
310
0
{
311
0
  char *oli;        /* option letter list index */
312
0
  int optchar, short_too;
313
0
  static int posixly_correct = -1;
314
315
0
  if (options == NULL)
316
0
    return (-1);
317
318
  /*
319
   * XXX Some GNU programs (like cvs) set optind to 0 instead of
320
   * XXX using optreset.  Work around this braindamage.
321
   */
322
0
  if (optind == 0)
323
0
    optind = optreset = 1;
324
325
  /*
326
   * Disable GNU extensions if POSIXLY_CORRECT is set or options
327
   * string begins with a '+'.
328
   */
329
0
  if (posixly_correct == -1 || optreset)
330
0
    posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
331
0
  if (*options == '-')
332
0
    flags |= FLAG_ALLARGS;
333
0
  else if (posixly_correct || *options == '+')
334
0
    flags &= ~FLAG_PERMUTE;
335
0
  if (*options == '+' || *options == '-')
336
0
    options++;
337
338
0
  optarg = NULL;
339
0
  if (optreset)
340
0
    nonopt_start = nonopt_end = -1;
341
0
start:
342
0
  if (optreset || !*place) {   /* update scanning pointer */
343
0
    optreset = 0;
344
0
    if (optind >= nargc) {          /* end of argument vector */
345
0
      place = EMSG;
346
0
      if (nonopt_end != -1) {
347
        /* do permutation, if we have to */
348
0
        permute_args(nonopt_start, nonopt_end,
349
0
            optind, nargv);
350
0
        optind -= nonopt_end - nonopt_start;
351
0
      }
352
0
      else if (nonopt_start != -1) {
353
        /*
354
         * If we skipped non-options, set optind
355
         * to the first of them.
356
         */
357
0
        optind = nonopt_start;
358
0
      }
359
0
      nonopt_start = nonopt_end = -1;
360
0
      return (-1);
361
0
    }
362
0
    if (*(place = nargv[optind]) != '-' ||
363
0
        (place[1] == '\0' && strchr(options, '-') == NULL)) {
364
0
      place = EMSG;   /* found non-option */
365
0
      if (flags & FLAG_ALLARGS) {
366
        /*
367
         * GNU extension:
368
         * return non-option as argument to option 1
369
         */
370
0
        optarg = nargv[optind++];
371
0
        return (INORDER);
372
0
      }
373
0
      if (!(flags & FLAG_PERMUTE)) {
374
        /*
375
         * If no permutation wanted, stop parsing
376
         * at first non-option.
377
         */
378
0
        return (-1);
379
0
      }
380
      /* do permutation */
381
0
      if (nonopt_start == -1)
382
0
        nonopt_start = optind;
383
0
      else if (nonopt_end != -1) {
384
0
        permute_args(nonopt_start, nonopt_end,
385
0
            optind, nargv);
386
0
        nonopt_start = optind -
387
0
            (nonopt_end - nonopt_start);
388
0
        nonopt_end = -1;
389
0
      }
390
0
      optind++;
391
      /* process next argument */
392
0
      goto start;
393
0
    }
394
0
    if (nonopt_start != -1 && nonopt_end == -1)
395
0
      nonopt_end = optind;
396
397
    /*
398
     * If we have "-" do nothing, if "--" we are done.
399
     */
400
0
    if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
401
0
      optind++;
402
0
      place = EMSG;
403
      /*
404
       * We found an option (--), so if we skipped
405
       * non-options, we have to permute.
406
       */
407
0
      if (nonopt_end != -1) {
408
0
        permute_args(nonopt_start, nonopt_end,
409
0
            optind, nargv);
410
0
        optind -= nonopt_end - nonopt_start;
411
0
      }
412
0
      nonopt_start = nonopt_end = -1;
413
0
      return (-1);
414
0
    }
415
0
  }
416
417
  /*
418
   * Check long options if:
419
   *  1) we were passed some
420
   *  2) the arg is not just "-"
421
   *  3) either the arg starts with -- we are getopt_long_only()
422
   */
423
0
  if (long_options != NULL && place != nargv[optind] &&
424
0
      (*place == '-' || (flags & FLAG_LONGONLY))) {
425
0
    short_too = 0;
426
0
    if (*place == '-')
427
0
      place++;   /* --foo long option */
428
0
    else if (*place != ':' && strchr(options, *place) != NULL)
429
0
      short_too = 1;   /* could be short option too */
430
431
0
    optchar = parse_long_options(nargv, options, long_options,
432
0
        idx, short_too);
433
0
    if (optchar != -1) {
434
0
      place = EMSG;
435
0
      return (optchar);
436
0
    }
437
0
  }
438
439
0
  if ((optchar = (int)*place++) == (int)':' ||
440
0
      (optchar == (int)'-' && *place != '\0') ||
441
0
      (oli = strchr(options, optchar)) == NULL) {
442
    /*
443
     * If the user specified "-" and  '-' isn't listed in
444
     * options, return -1 (non-option) as per POSIX.
445
     * Otherwise, it is an unknown option character (or ':').
446
     */
447
0
    if (optchar == (int)'-' && *place == '\0')
448
0
      return (-1);
449
0
    if (!*place)
450
0
      ++optind;
451
0
    if (PRINT_ERROR)
452
0
      warnx(illoptchar, optchar);
453
0
    optopt = optchar;
454
0
    return (BADCH);
455
0
  }
456
0
  if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
457
    /* -W long-option */
458
0
    if (*place)     /* no space */
459
0
      /* NOTHING */;
460
0
    else if (++optind >= nargc) { /* no arg */
461
0
      place = EMSG;
462
0
      if (PRINT_ERROR)
463
0
        warnx(recargchar, optchar);
464
0
      optopt = optchar;
465
0
      return (BADARG);
466
0
    } else        /* white space */
467
0
      place = nargv[optind];
468
0
    optchar = parse_long_options(nargv, options, long_options,
469
0
        idx, 0);
470
0
    place = EMSG;
471
0
    return (optchar);
472
0
  }
473
0
  if (*++oli != ':') {     /* doesn't take argument */
474
0
    if (!*place)
475
0
      ++optind;
476
0
  } else {       /* takes (optional) argument */
477
0
    optarg = NULL;
478
0
    if (*place)     /* no white space */
479
0
      optarg = place;
480
0
    else if (oli[1] != ':') { /* arg not optional */
481
0
      if (++optind >= nargc) { /* no arg */
482
0
        place = EMSG;
483
0
        if (PRINT_ERROR)
484
0
          warnx(recargchar, optchar);
485
0
        optopt = optchar;
486
0
        return (BADARG);
487
0
      } else
488
0
        optarg = nargv[optind];
489
0
    }
490
0
    place = EMSG;
491
0
    ++optind;
492
0
  }
493
  /* dump back option letter */
494
0
  return (optchar);
495
0
}
496
497
/*
498
 * getopt --
499
 *  Parse argc/argv argument vector.
500
 *
501
 * [eventually this will replace the BSD getopt]
502
 */
503
int
504
getopt(int nargc, char * const *nargv, const char *options)
505
0
{
506
507
  /*
508
   * We don't pass FLAG_PERMUTE to getopt_internal() since
509
   * the BSD getopt(3) (unlike GNU) has never done this.
510
   *
511
   * Furthermore, since many privileged programs call getopt()
512
   * before dropping privileges it makes sense to keep things
513
   * as simple (and bug-free) as possible.
514
   */
515
0
  return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
516
0
}
517
518
#if 0
519
/*
520
 * getopt_long --
521
 *  Parse argc/argv argument vector.
522
 */
523
int
524
getopt_long(int nargc, char * const *nargv, const char *options,
525
    const struct option *long_options, int *idx)
526
{
527
528
  return (getopt_internal(nargc, nargv, options, long_options, idx,
529
      FLAG_PERMUTE));
530
}
531
532
/*
533
 * getopt_long_only --
534
 *  Parse argc/argv argument vector.
535
 */
536
int
537
getopt_long_only(int nargc, char * const *nargv, const char *options,
538
    const struct option *long_options, int *idx)
539
{
540
541
  return (getopt_internal(nargc, nargv, options, long_options, idx,
542
      FLAG_PERMUTE|FLAG_LONGONLY));
543
}
544
#endif
545
546
#endif /* !defined(HAVE_GETOPT) || !defined(HAVE_OPTRESET) */