xref: /PHP-8.4/main/getopt.c (revision ec3de14a)
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