Coverage Report

Created: 2026-01-24 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/isc/commandline.c
Line
Count
Source
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0 AND BSD-3-Clause
5
6
 * This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
 */
10
11
/*
12
 * Copyright (C) 1987, 1993, 1994 The Regents of the University of California.
13
 *
14
 * Redistribution and use in source and binary forms, with or without
15
 * modification, are permitted provided that the following conditions
16
 * are met:
17
 * 1. Redistributions of source code must retain the above copyright
18
 *    notice, this list of conditions and the following disclaimer.
19
 * 2. Redistributions in binary form must reproduce the above copyright
20
 *    notice, this list of conditions and the following disclaimer in the
21
 *    documentation and/or other materials provided with the distribution.
22
 * 3. Neither the name of the University nor the names of its contributors
23
 *    may be used to endorse or promote products derived from this software
24
 *    without specific prior written permission.
25
 *
26
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36
 * SUCH DAMAGE.
37
 *
38
 * See the COPYRIGHT file distributed with this work for additional
39
 * information regarding copyright ownership.
40
 */
41
42
/*! \file
43
 * This file was adapted from the NetBSD project's source tree, RCS ID:
44
 *    NetBSD: getopt.c,v 1.15 1999/09/20 04:39:37 lukem Exp
45
 *
46
 * The primary change has been to rename items to the ISC namespace
47
 * and format in the ISC coding style.
48
 */
49
50
#include <limits.h>
51
#include <stdbool.h>
52
#include <stdio.h>
53
54
#include <isc/commandline.h>
55
#include <isc/file.h>
56
#include <isc/mem.h>
57
#include <isc/string.h>
58
#include <isc/util.h>
59
60
/*% Index into parent argv vector. */
61
int isc_commandline_index = 1;
62
/*% Character checked for validity. */
63
int isc_commandline_option;
64
/*% Argument associated with option. */
65
char *isc_commandline_argument;
66
/*% For printing error messages. */
67
char isc_commandline_progname[NAME_MAX];
68
/*% Print error messages. */
69
bool isc_commandline_errprint = true;
70
/*% Reset processing. */
71
bool isc_commandline_reset = true;
72
73
void
74
0
isc_commandline_init(int argc ISC_ATTR_UNUSED, char *const *argv) {
75
0
  isc_file_progname(argv[0], isc_commandline_progname,
76
0
        sizeof(isc_commandline_progname));
77
0
}
78
79
static char endopt = '\0';
80
81
0
#define BADOPT '?'
82
0
#define BADARG ':'
83
0
#define ENDOPT &endopt
84
85
/*!
86
 * getopt --
87
 *  Parse argc/argv argument vector.
88
 */
89
int
90
0
isc_commandline_parse(int argc, char *const *argv, const char *options) {
91
0
  static char *place = ENDOPT;
92
0
  const char *option; /* Index into *options of option. */
93
94
0
  REQUIRE(argc >= 0 && argv != NULL && options != NULL);
95
96
  /*
97
   * Update scanning pointer, either because a reset was requested or
98
   * the previous argv was finished.
99
   */
100
0
  if (isc_commandline_reset || *place == '\0') {
101
0
    if (isc_commandline_reset) {
102
0
      isc_commandline_index = 1;
103
0
      isc_commandline_reset = false;
104
0
    }
105
106
0
    if (isc_commandline_index >= argc ||
107
0
        *(place = argv[isc_commandline_index]) != '-')
108
0
    {
109
      /*
110
       * Index out of range or points to non-option.
111
       */
112
0
      place = ENDOPT;
113
0
      return -1;
114
0
    }
115
116
0
    if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
117
      /*
118
       * Found '--' to signal end of options.  Advance
119
       * index to next argv, the first non-option.
120
       */
121
0
      isc_commandline_index++;
122
0
      place = ENDOPT;
123
0
      return -1;
124
0
    }
125
0
  }
126
127
0
  isc_commandline_option = *place++;
128
0
  option = strchr(options, isc_commandline_option);
129
130
  /*
131
   * Ensure valid option has been passed as specified by options string.
132
   * '-:' is never a valid command line option because it could not
133
   * distinguish ':' from the argument specifier in the options string.
134
   */
135
0
  if (isc_commandline_option == ':' || option == NULL) {
136
0
    if (*place == '\0') {
137
0
      isc_commandline_index++;
138
0
    }
139
140
0
    if (isc_commandline_errprint && *options != ':') {
141
0
      fprintf(stderr, "%s: illegal option -- %c\n",
142
0
        isc_commandline_progname,
143
0
        isc_commandline_option);
144
0
    }
145
146
0
    return BADOPT;
147
0
  }
148
149
0
  if (*++option != ':') {
150
    /*
151
     * Option does not take an argument.
152
     */
153
0
    isc_commandline_argument = NULL;
154
155
    /*
156
     * Skip to next argv if at the end of the current argv.
157
     */
158
0
    if (*place == '\0') {
159
0
      ++isc_commandline_index;
160
0
    }
161
0
  } else {
162
    /*
163
     * Option needs an argument.
164
     */
165
0
    if (*place != '\0') {
166
      /*
167
       * Option is in this argv, -D1 style.
168
       */
169
0
      isc_commandline_argument = place;
170
0
    } else if (argc > ++isc_commandline_index) {
171
      /*
172
       * Option is next argv, -D 1 style.
173
       */
174
0
      isc_commandline_argument = argv[isc_commandline_index];
175
0
    } else {
176
      /*
177
       * Argument needed, but no more argv.
178
       */
179
0
      place = ENDOPT;
180
181
      /*
182
       * Silent failure with "missing argument" return
183
       * when ':' starts options string, per historical spec.
184
       */
185
0
      if (*options == ':') {
186
0
        return BADARG;
187
0
      }
188
189
0
      if (isc_commandline_errprint) {
190
0
        fprintf(stderr,
191
0
          "%s: option requires an argument -- "
192
0
          "%c\n",
193
0
          isc_commandline_progname,
194
0
          isc_commandline_option);
195
0
      }
196
197
0
      return BADOPT;
198
0
    }
199
200
0
    place = ENDOPT;
201
202
    /*
203
     * Point to argv that follows argument.
204
     */
205
0
    isc_commandline_index++;
206
0
  }
207
208
0
  return isc_commandline_option;
209
0
}
210
211
isc_result_t
212
isc_commandline_strtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp,
213
0
        char ***argvp, unsigned int n) {
214
0
restart:
215
  /* Discard leading whitespace. */
216
0
  while (*s == ' ' || *s == '\t') {
217
0
    s++;
218
0
  }
219
220
0
  if (*s == '\0') {
221
    /* We have reached the end of the string. */
222
0
    *argcp = n;
223
0
    *argvp = isc_mem_cget(mctx, n, sizeof(char *));
224
0
  } else {
225
0
    char *p = s;
226
0
    while (*p != ' ' && *p != '\t' && *p != '\0' && *p != '{') {
227
0
      if (*p == '\n') {
228
0
        *p = ' ';
229
0
        goto restart;
230
0
      }
231
0
      p++;
232
0
    }
233
234
    /* do "grouping", items between { and } are one arg */
235
0
    if (*p == '{') {
236
0
      char *t = p;
237
      /*
238
       * shift all characters to left by 1 to get rid of '{'
239
       */
240
0
      while (*t != '\0') {
241
0
        t++;
242
0
        *(t - 1) = *t;
243
0
      }
244
0
      while (*p != '\0' && *p != '}') {
245
0
        p++;
246
0
      }
247
      /* get rid of '}' character */
248
0
      if (*p == '}') {
249
0
        *p = '\0';
250
0
        p++;
251
0
      }
252
      /* normal case, no "grouping" */
253
0
    } else if (*p != '\0') {
254
0
      *p++ = '\0';
255
0
    }
256
257
0
    RETERR(isc_commandline_strtoargv(mctx, p, argcp, argvp, n + 1));
258
0
    (*argvp)[n] = s;
259
0
  }
260
261
0
  return ISC_R_SUCCESS;
262
0
}