Blame oslib/getopt_long.c

Packit Service 0e769b
/*
Packit Service 0e769b
 * getopt.c
Packit Service 0e769b
 *
Packit Service 0e769b
 * getopt_long(), or at least a common subset thereof:
Packit Service 0e769b
 *
Packit Service 0e769b
 * - Option reordering is not supported
Packit Service 0e769b
 * - -W foo is not supported
Packit Service 0e769b
 * - First optstring character "-" not supported.
Packit Service 0e769b
 *
Packit Service 0e769b
 * This file was imported from the klibc library from hpa
Packit Service 0e769b
 */
Packit Service 0e769b
Packit Service 0e769b
#include <stdint.h>
Packit Service 0e769b
#include <unistd.h>
Packit Service 0e769b
#include <string.h>
Packit Service 0e769b
Packit Service 0e769b
#include "getopt.h"
Packit Service 0e769b
Packit Service 0e769b
char *optarg = NULL;
Packit Service 0e769b
int optind = 0, opterr = 0, optopt = 0;
Packit Service 0e769b
Packit Service 0e769b
static struct getopt_private_state {
Packit Service 0e769b
	const char *optptr;
Packit Service 0e769b
	const char *last_optstring;
Packit Service 0e769b
	char *const *last_argv;
Packit Service 0e769b
} pvt;
Packit Service 0e769b
Packit Service 0e769b
static inline const char *option_matches(const char *arg_str,
Packit Service 0e769b
					 const char *opt_name, int smatch)
Packit Service 0e769b
{
Packit Service 0e769b
	while (*arg_str != '\0' && *arg_str != '=') {
Packit Service 0e769b
		if (*arg_str++ != *opt_name++)
Packit Service 0e769b
			return NULL;
Packit Service 0e769b
	}
Packit Service 0e769b
Packit Service 0e769b
	if (*opt_name && !smatch)
Packit Service 0e769b
		return NULL;
Packit Service 0e769b
Packit Service 0e769b
	return arg_str;
Packit Service 0e769b
}
Packit Service 0e769b
Packit Service 0e769b
int getopt_long_only(int argc, char *const *argv, const char *optstring,
Packit Service 0e769b
		const struct option *longopts, int *longindex)
Packit Service 0e769b
{
Packit Service 0e769b
	const char *carg;
Packit Service 0e769b
	const char *osptr;
Packit Service 0e769b
	int opt;
Packit Service 0e769b
Packit Service 0e769b
	optarg = NULL;
Packit Service 0e769b
Packit Service 0e769b
	/* getopt() relies on a number of different global state
Packit Service 0e769b
	   variables, which can make this really confusing if there is
Packit Service 0e769b
	   more than one use of getopt() in the same program.  This
Packit Service 0e769b
	   attempts to detect that situation by detecting if the
Packit Service 0e769b
	   "optstring" or "argv" argument have changed since last time
Packit Service 0e769b
	   we were called; if so, reinitialize the query state. */
Packit Service 0e769b
Packit Service 0e769b
	if (optstring != pvt.last_optstring || argv != pvt.last_argv ||
Packit Service 0e769b
	    optind < 1 || optind > argc) {
Packit Service 0e769b
		/* optind doesn't match the current query */
Packit Service 0e769b
		pvt.last_optstring = optstring;
Packit Service 0e769b
		pvt.last_argv = argv;
Packit Service 0e769b
		optind = 1;
Packit Service 0e769b
		pvt.optptr = NULL;
Packit Service 0e769b
	}
Packit Service 0e769b
Packit Service 0e769b
	carg = argv[optind];
Packit Service 0e769b
Packit Service 0e769b
	/* First, eliminate all non-option cases */
Packit Service 0e769b
Packit Service 0e769b
	if (!carg || carg[0] != '-' || !carg[1])
Packit Service 0e769b
		return -1;
Packit Service 0e769b
Packit Service 0e769b
	if (carg[1] == '-') {
Packit Service 0e769b
		const struct option *lo;
Packit Service 0e769b
		const char *opt_end = NULL;
Packit Service 0e769b
Packit Service 0e769b
		optind++;
Packit Service 0e769b
Packit Service 0e769b
		/* Either it's a long option, or it's -- */
Packit Service 0e769b
		if (!carg[2]) {
Packit Service 0e769b
			/* It's -- */
Packit Service 0e769b
			return -1;
Packit Service 0e769b
		}
Packit Service 0e769b
Packit Service 0e769b
		for (lo = longopts; lo->name; lo++) {
Packit Service 0e769b
			opt_end = option_matches(carg+2, lo->name, 0);
Packit Service 0e769b
			if (opt_end)
Packit Service 0e769b
			    break;
Packit Service 0e769b
		}
Packit Service 0e769b
		/*
Packit Service 0e769b
		 * The GNU getopt_long_only() apparently allows a short match,
Packit Service 0e769b
		 * if it's unique and if we don't have a full match. Let's
Packit Service 0e769b
		 * do the same here, search and see if there is one (and only
Packit Service 0e769b
		 * one) short match.
Packit Service 0e769b
		 */
Packit Service 0e769b
		if (!opt_end) {
Packit Service 0e769b
			const struct option *lo_match = NULL;
Packit Service 0e769b
Packit Service 0e769b
			for (lo = longopts; lo->name; lo++) {
Packit Service 0e769b
				const char *ret;
Packit Service 0e769b
Packit Service 0e769b
				ret = option_matches(carg+2, lo->name, 1);
Packit Service 0e769b
				if (!ret)
Packit Service 0e769b
					continue;
Packit Service 0e769b
				if (!opt_end) {
Packit Service 0e769b
					opt_end = ret;
Packit Service 0e769b
					lo_match = lo;
Packit Service 0e769b
				} else {
Packit Service 0e769b
					opt_end = NULL;
Packit Service 0e769b
					break;
Packit Service 0e769b
				}
Packit Service 0e769b
			}
Packit Service 0e769b
			if (!opt_end)
Packit Service 0e769b
				return '?';
Packit Service 0e769b
			lo = lo_match;
Packit Service 0e769b
		}
Packit Service 0e769b
Packit Service 0e769b
		if (longindex)
Packit Service 0e769b
			*longindex = lo-longopts;
Packit Service 0e769b
Packit Service 0e769b
		if (*opt_end == '=') {
Packit Service 0e769b
			if (lo->has_arg)
Packit Service 0e769b
				optarg = (char *)opt_end+1;
Packit Service 0e769b
			else
Packit Service 0e769b
				return '?';
Packit Service 0e769b
		} else if (lo->has_arg == 1) {
Packit Service 0e769b
			if (!(optarg = argv[optind]))
Packit Service 0e769b
				return '?';
Packit Service 0e769b
			optind++;
Packit Service 0e769b
		}
Packit Service 0e769b
Packit Service 0e769b
		if (lo->flag) {
Packit Service 0e769b
			*lo->flag = lo->val;
Packit Service 0e769b
			return 0;
Packit Service 0e769b
		} else {
Packit Service 0e769b
			return lo->val;
Packit Service 0e769b
		}
Packit Service 0e769b
	}
Packit Service 0e769b
Packit Service 0e769b
	if ((uintptr_t) (pvt.optptr - carg) > (uintptr_t) strlen(carg)) {
Packit Service 0e769b
		/* Someone frobbed optind, change to new opt. */
Packit Service 0e769b
		pvt.optptr = carg + 1;
Packit Service 0e769b
	}
Packit Service 0e769b
Packit Service 0e769b
	opt = *pvt.optptr++;
Packit Service 0e769b
Packit Service 0e769b
	if (opt != ':' && (osptr = strchr(optstring, opt))) {
Packit Service 0e769b
		if (osptr[1] == ':') {
Packit Service 0e769b
			if (*pvt.optptr) {
Packit Service 0e769b
				/* Argument-taking option with attached
Packit Service 0e769b
				   argument */
Packit Service 0e769b
				optarg = (char *)pvt.optptr;
Packit Service 0e769b
				optind++;
Packit Service 0e769b
			} else {
Packit Service 0e769b
				/* Argument-taking option with non-attached
Packit Service 0e769b
				   argument */
Packit Service 0e769b
				if (osptr[2] == ':') {
Packit Service 0e769b
					if (argv[optind + 1]) {
Packit Service 0e769b
						optarg = (char *)argv[optind+1];
Packit Service 0e769b
						optind += 2;
Packit Service 0e769b
					} else {
Packit Service 0e769b
						optarg = NULL;
Packit Service 0e769b
						optind++;
Packit Service 0e769b
					}
Packit Service 0e769b
					return opt;
Packit Service 0e769b
				} else if (argv[optind + 1]) {
Packit Service 0e769b
					optarg = (char *)argv[optind+1];
Packit Service 0e769b
					optind += 2;
Packit Service 0e769b
				} else {
Packit Service 0e769b
					/* Missing argument */
Packit Service 0e769b
					optind++;
Packit Service 0e769b
					return (optstring[0] == ':')
Packit Service 0e769b
						? ':' : '?';
Packit Service 0e769b
				}
Packit Service 0e769b
			}
Packit Service 0e769b
			return opt;
Packit Service 0e769b
		} else {
Packit Service 0e769b
			/* Non-argument-taking option */
Packit Service 0e769b
			/* pvt.optptr will remember the exact position to
Packit Service 0e769b
			   resume at */
Packit Service 0e769b
			if (!*pvt.optptr)
Packit Service 0e769b
				optind++;
Packit Service 0e769b
			return opt;
Packit Service 0e769b
		}
Packit Service 0e769b
	} else {
Packit Service 0e769b
		/* Unknown option */
Packit Service 0e769b
		optopt = opt;
Packit Service 0e769b
		if (!*pvt.optptr)
Packit Service 0e769b
			optind++;
Packit Service 0e769b
		return '?';
Packit Service 0e769b
	}
Packit Service 0e769b
}