176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * getopt.c
376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * getopt_long(), or at least a common subset thereof:
576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * - Option reordering is not supported
776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * - -W foo is not supported
876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * - First optstring character "-" not supported.
976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
1076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
1176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdint.h>
1276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <unistd.h>
1376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <string.h>
1476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <getopt.h>
1576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
1676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanchar *optarg;
1776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint optind, opterr, optopt;
1876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct getopt_private_state {
1976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	const char *optptr;
2076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	const char *last_optstring;
2176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	char *const *last_argv;
2276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} pvt;
2376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
2476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic inline const char *option_matches(const char *arg_str,
2576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					 const char *opt_name)
2676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
2776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	while (*arg_str != '\0' && *arg_str != '=') {
2876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (*arg_str++ != *opt_name++)
2976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			return NULL;
3076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
3176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
3276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (*opt_name)
3376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return NULL;
3476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
3576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return arg_str;
3676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
3776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
3876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint getopt_long(int argc, char *const *argv, const char *optstring,
3976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		const struct option *longopts, int *longindex)
4076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
4176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	const char *carg;
4276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	const char *osptr;
4376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int opt;
4476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
4576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* getopt() relies on a number of different global state
4676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	   variables, which can make this really confusing if there is
4776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	   more than one use of getopt() in the same program.  This
4876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	   attempts to detect that situation by detecting if the
4976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	   "optstring" or "argv" argument have changed since last time
5076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	   we were called; if so, reinitialize the query state. */
5176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
5276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (optstring != pvt.last_optstring || argv != pvt.last_argv ||
5376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	    optind < 1 || optind > argc) {
5476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* optind doesn't match the current query */
5576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pvt.last_optstring = optstring;
5676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pvt.last_argv = argv;
5776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		optind = 1;
5876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pvt.optptr = NULL;
5976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
6076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	carg = argv[optind];
6276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* First, eliminate all non-option cases */
6476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (!carg || carg[0] != '-' || !carg[1])
6676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return -1;
6776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (carg[1] == '-') {
6976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		const struct option *lo;
7076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		const char *opt_end = NULL;
7176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
7276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		optind++;
7376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
7476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Either it's a long option, or it's -- */
7576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (!carg[2]) {
7676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			/* It's -- */
7776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			return -1;
7876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
7976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
8076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		for (lo = longopts; lo->name; lo++) {
8176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			if ((opt_end = option_matches(carg+2, lo->name)))
8276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			    break;
8376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
8476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (!opt_end)
8576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			return '?';
8676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
8776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (longindex)
8876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			*longindex = lo-longopts;
8976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
9076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (*opt_end == '=') {
9176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			if (lo->has_arg)
9276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				optarg = (char *)opt_end+1;
9376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			else
9476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				return '?';
9576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		} else if (lo->has_arg == 1) {
9676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			if (!(optarg = argv[optind]))
9776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				return '?';
9876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			optind++;
9976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
10076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
10176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (lo->flag) {
10276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			*lo->flag = lo->val;
10376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			return 0;
10476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		} else {
10576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			return lo->val;
10676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
10776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
10876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
10976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ((uintptr_t) (pvt.optptr - carg) > (uintptr_t) strlen(carg)) {
11076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Someone frobbed optind, change to new opt. */
11176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		pvt.optptr = carg + 1;
11276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
11376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
11476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	opt = *pvt.optptr++;
11576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
11676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if (opt != ':' && (osptr = strchr(optstring, opt))) {
11776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (osptr[1] == ':') {
11876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			if (*pvt.optptr) {
11976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				/* Argument-taking option with attached
12076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				   argument */
12176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				optarg = (char *)pvt.optptr;
12276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				optind++;
12376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			} else {
12476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				/* Argument-taking option with non-attached
12576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				   argument */
12676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				if (argv[optind + 1]) {
12776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					optarg = (char *)argv[optind+1];
12876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					optind += 2;
12976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				} else {
13076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					/* Missing argument */
13176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					optind++;
13276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					return (optstring[0] == ':')
13376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman						? ':' : '?';
13476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				}
13576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			}
13676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			return opt;
13776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		} else {
13876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			/* Non-argument-taking option */
13976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			/* pvt.optptr will remember the exact position to
14076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			   resume at */
14176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			if (!*pvt.optptr)
14276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				optind++;
14376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			return opt;
14476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
14576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	} else {
14676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Unknown option */
14776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		optopt = opt;
14876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if (!*pvt.optptr)
14976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			optind++;
15076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return '?';
15176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
15276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
153