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