1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2016 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/* $Id$ */
20
21#include <stdio.h>
22#include <string.h>
23#include <assert.h>
24#include <stdlib.h>
25#include "php_getopt.h"
26
27#define OPTERRCOLON (1)
28#define OPTERRNF (2)
29#define OPTERRARG (3)
30
31static int php_opt_error(int argc, char * const *argv, int oint, int optchr, int err, int show_err) /* {{{ */
32{
33	if (show_err)
34	{
35		fprintf(stderr, "Error in argument %d, char %d: ", oint, optchr+1);
36		switch(err)
37		{
38		case OPTERRCOLON:
39			fprintf(stderr, ": in flags\n");
40			break;
41		case OPTERRNF:
42			fprintf(stderr, "option not found %c\n", argv[oint][optchr]);
43			break;
44		case OPTERRARG:
45			fprintf(stderr, "no argument for option %c\n", argv[oint][optchr]);
46			break;
47		default:
48			fprintf(stderr, "unknown\n");
49			break;
50		}
51	}
52	return('?');
53}
54/* }}} */
55
56PHPAPI int php_optidx = -1;
57
58PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char **optarg, int *optind, int show_err, int arg_start) /* {{{ */
59{
60	static int optchr = 0;
61	static int dash = 0; /* have already seen the - */
62	static char **prev_optarg = NULL;
63
64	php_optidx = -1;
65
66	if(prev_optarg && prev_optarg != optarg) {
67		/* reset the state */
68		optchr = 0;
69		dash = 0;
70	}
71	prev_optarg = optarg;
72
73	if (*optind >= argc) {
74		return(EOF);
75	}
76	if (!dash) {
77		if ((argv[*optind][0] !=  '-')) {
78			return(EOF);
79		} else {
80			if (!argv[*optind][1])
81			{
82				/*
83				* use to specify stdin. Need to let pgm process this and
84				* the following args
85				*/
86				return(EOF);
87			}
88		}
89	}
90	if ((argv[*optind][0] == '-') && (argv[*optind][1] == '-')) {
91		const char *pos;
92		int arg_end = (int)strlen(argv[*optind])-1;
93
94		/* '--' indicates end of args if not followed by a known long option name */
95		if (argv[*optind][2] == '\0') {
96			(*optind)++;
97			return(EOF);
98		}
99
100		arg_start = 2;
101
102		/* Check for <arg>=<val> */
103		if ((pos = php_memnstr(&argv[*optind][arg_start], "=", 1, argv[*optind]+arg_end)) != NULL) {
104			arg_end = pos-&argv[*optind][arg_start];
105			arg_start++;
106		} else {
107			arg_end--;
108		}
109
110		while (1) {
111			php_optidx++;
112			if (opts[php_optidx].opt_char == '-') {
113				(*optind)++;
114				return(php_opt_error(argc, argv, *optind-1, optchr, OPTERRARG, show_err));
115			} 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)) {
116				break;
117			}
118		}
119
120		optchr = 0;
121		dash = 0;
122		arg_start += (int)strlen(opts[php_optidx].opt_name);
123	} else {
124		if (!dash) {
125			dash = 1;
126			optchr = 1;
127		}
128		/* Check if the guy tries to do a -: kind of flag */
129		if (argv[*optind][optchr] == ':') {
130			dash = 0;
131			(*optind)++;
132			return (php_opt_error(argc, argv, *optind-1, optchr, OPTERRCOLON, show_err));
133		}
134		arg_start = 1 + optchr;
135	}
136	if (php_optidx < 0) {
137		while (1) {
138			php_optidx++;
139			if (opts[php_optidx].opt_char == '-') {
140				int errind = *optind;
141				int errchr = optchr;
142
143				if (!argv[*optind][optchr+1]) {
144					dash = 0;
145					(*optind)++;
146				} else {
147					optchr++;
148					arg_start++;
149				}
150				return(php_opt_error(argc, argv, errind, errchr, OPTERRNF, show_err));
151			} else if (argv[*optind][optchr] == opts[php_optidx].opt_char) {
152				break;
153			}
154		}
155	}
156	if (opts[php_optidx].need_param) {
157		/* Check for cases where the value of the argument
158		is in the form -<arg> <val>, -<arg>=<varl> or -<arg><val> */
159		dash = 0;
160		if (!argv[*optind][arg_start]) {
161			(*optind)++;
162			if (*optind == argc) {
163				/* Was the value required or is it optional? */
164				if (opts[php_optidx].need_param == 1) {
165					return(php_opt_error(argc, argv, *optind-1, optchr, OPTERRARG, show_err));
166				}
167			/* Optional value is not supported with -<arg> <val> style */
168			} else if (opts[php_optidx].need_param == 1) {
169				*optarg = argv[(*optind)++];
170 			}
171		} else if (argv[*optind][arg_start] == '=') {
172			arg_start++;
173			*optarg = &argv[*optind][arg_start];
174			(*optind)++;
175		} else {
176			*optarg = &argv[*optind][arg_start];
177			(*optind)++;
178		}
179		return opts[php_optidx].opt_char;
180	} else {
181		/* multiple options specified as one (exclude long opts) */
182		if (arg_start >= 2 && !((argv[*optind][0] == '-') && (argv[*optind][1] == '-'))) {
183			if (!argv[*optind][optchr+1])
184			{
185				dash = 0;
186				(*optind)++;
187			} else {
188				optchr++;
189			}
190		} else {
191			(*optind)++;
192		}
193		return opts[php_optidx].opt_char;
194	}
195	assert(0);
196	return(0);	/* never reached */
197}
198/* }}} */
199
200/*
201 * Local variables:
202 * tab-width: 4
203 * c-basic-offset: 4
204 * End:
205 * vim600: sw=4 ts=4 fdm=marker
206 * vim<600: sw=4 ts=4
207 */
208