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