Coverage Report

Created: 2026-06-02 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/main/getopt.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright © The PHP Group and Contributors.                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to the Modified BSD License that is      |
6
   | bundled with this package in the file LICENSE, and is available      |
7
   | through the World Wide Web at <https://www.php.net/license/>.        |
8
   |                                                                      |
9
   | SPDX-License-Identifier: BSD-3-Clause                                |
10
   +----------------------------------------------------------------------+
11
   | Author: Marcus Boerger <helly@php.net>                               |
12
   +----------------------------------------------------------------------+
13
*/
14
15
#include <stdio.h>
16
#include <string.h>
17
#include <assert.h>
18
#include <stdlib.h>
19
#include "php_getopt.h"
20
21
0
#define OPTERRCOLON 1
22
0
#define OPTERRNF 2
23
0
#define OPTERRARG 3
24
25
// Print error message to stderr and return -2 to distinguish it from '?' command line option.
26
static int php_opt_error(int argc, char * const *argv, int optint, int optchr, int err, int show_err) /* {{{ */
27
0
{
28
0
  if (show_err) {
29
0
    fprintf(stderr, "Error in argument %d, char %d: ", optint, optchr+1);
30
0
    switch(err)
31
0
    {
32
0
    case OPTERRCOLON:
33
0
      fprintf(stderr, ": in flags\n");
34
0
      break;
35
0
    case OPTERRNF:
36
0
      fprintf(stderr, "option not found %c\n", argv[optint][optchr]);
37
0
      break;
38
0
    case OPTERRARG:
39
0
      fprintf(stderr, "no argument for option %c\n", argv[optint][optchr]);
40
0
      break;
41
0
    default:
42
0
      fprintf(stderr, "unknown\n");
43
0
      break;
44
0
    }
45
0
  }
46
0
  return PHP_GETOPT_INVALID_ARG;
47
0
}
48
/* }}} */
49
50
PHPAPI int php_optidx = -1;
51
52
PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char **optarg, int *optind, int show_err, int arg_start) /* {{{ */
53
0
{
54
0
  static int optchr = 0;
55
0
  static int dash = 0; /* have already seen the - */
56
0
  static char **prev_optarg = NULL;
57
58
0
  php_optidx = -1;
59
0
  if (optarg) {
60
0
    *optarg = NULL;
61
0
  }
62
63
0
  if (prev_optarg && prev_optarg != optarg) {
64
    /* reset the state */
65
0
    optchr = 0;
66
0
    dash = 0;
67
0
  }
68
0
  prev_optarg = optarg;
69
70
0
  if (*optind >= argc) {
71
0
    return EOF;
72
0
  }
73
0
  if (!dash) {
74
0
    if (argv[*optind][0] !=  '-') {
75
0
      return EOF;
76
0
    }
77
0
    if (!argv[*optind][1]) {
78
      /*
79
      * use to specify stdin. Need to let pgm process this and
80
      * the following args
81
      */
82
0
      return EOF;
83
0
    }
84
0
  }
85
0
  if ((argv[*optind][0] == '-') && (argv[*optind][1] == '-')) {
86
0
    const char *pos;
87
0
    size_t arg_end = strlen(argv[*optind])-1;
88
89
    /* '--' indicates end of args if not followed by a known long option name */
90
0
    if (argv[*optind][2] == '\0') {
91
0
      (*optind)++;
92
0
      return EOF;
93
0
    }
94
95
0
    arg_start = 2;
96
97
    /* Check for <arg>=<val> */
98
0
    if ((pos = memchr(&argv[*optind][arg_start], '=', arg_end - arg_start)) != NULL) {
99
0
      arg_end = pos-&argv[*optind][arg_start];
100
0
      arg_start++;
101
0
    } else {
102
0
      arg_end--;
103
0
    }
104
105
0
    while (1) {
106
0
      php_optidx++;
107
0
      if (opts[php_optidx].opt_char == '-') {
108
0
        (*optind)++;
109
0
        return php_opt_error(argc, argv, *optind-1, optchr, OPTERRARG, show_err);
110
0
      } else if (opts[php_optidx].opt_name && !strncmp(&argv[*optind][2], opts[php_optidx].opt_name, arg_end) && arg_end == strlen(opts[php_optidx].opt_name)) {
111
0
        break;
112
0
      }
113
0
    }
114
115
0
    optchr = 0;
116
0
    dash = 0;
117
0
    arg_start += (int)strlen(opts[php_optidx].opt_name);
118
0
  } else {
119
0
    if (!dash) {
120
0
      dash = 1;
121
0
      optchr = 1;
122
0
    }
123
    /* Check if the guy tries to do a -: kind of flag */
124
0
    if (argv[*optind][optchr] == ':') {
125
0
      dash = 0;
126
0
      (*optind)++;
127
0
      return php_opt_error(argc, argv, *optind-1, optchr, OPTERRCOLON, show_err);
128
0
    }
129
0
    arg_start = 1 + optchr;
130
0
  }
131
0
  if (php_optidx < 0) {
132
0
    while (1) {
133
0
      php_optidx++;
134
0
      if (opts[php_optidx].opt_char == '-') {
135
0
        int errind = *optind;
136
0
        int errchr = optchr;
137
138
0
        if (!argv[*optind][optchr+1]) {
139
0
          dash = 0;
140
0
          (*optind)++;
141
0
        } else {
142
0
          optchr++;
143
0
          arg_start++;
144
0
        }
145
0
        return php_opt_error(argc, argv, errind, errchr, OPTERRNF, show_err);
146
0
      } else if (argv[*optind][optchr] == opts[php_optidx].opt_char) {
147
0
        break;
148
0
      }
149
0
    }
150
0
  }
151
0
  if (opts[php_optidx].need_param) {
152
    /* Check for cases where the value of the argument
153
    is in the form -<arg> <val>, -<arg>=<varl> or -<arg><val> */
154
0
    dash = 0;
155
0
    if (!argv[*optind][arg_start]) {
156
0
      (*optind)++;
157
0
      if (*optind == argc) {
158
        /* Was the value required or is it optional? */
159
0
        if (opts[php_optidx].need_param == 1) {
160
0
          return php_opt_error(argc, argv, *optind-1, optchr, OPTERRARG, show_err);
161
0
        }
162
      /* Optional value is not supported with -<arg> <val> style */
163
0
      } else if (opts[php_optidx].need_param == 1) {
164
0
        *optarg = argv[(*optind)++];
165
0
      }
166
0
    } else if (argv[*optind][arg_start] == '=') {
167
0
      arg_start++;
168
0
      *optarg = &argv[*optind][arg_start];
169
0
      (*optind)++;
170
0
    } else {
171
0
      *optarg = &argv[*optind][arg_start];
172
0
      (*optind)++;
173
0
    }
174
0
    return opts[php_optidx].opt_char;
175
0
  } else {
176
    /* multiple options specified as one (exclude long opts) */
177
0
    if (arg_start >= 2 && !((argv[*optind][0] == '-') && (argv[*optind][1] == '-'))) {
178
0
      if (!argv[*optind][optchr+1]) {
179
0
        dash = 0;
180
0
        (*optind)++;
181
0
      } else {
182
0
        optchr++;
183
0
      }
184
0
    } else {
185
0
      (*optind)++;
186
0
    }
187
0
    return opts[php_optidx].opt_char;
188
0
  }
189
0
  assert(0);
190
0
  return 0; /* never reached */
191
0
}
192
/* }}} */