1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 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        char *pos;
92        int arg_end = 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 += 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