Sigrid Solveig Haflínudóttirit's a first4a89e3d4 years agoHistory
354 lines · 7.2 KB · C
RawDownload
/* * parg - parse argv * * Written in 2015-2016 by Joergen Ibsen * * To the extent possible under law, the author(s) have dedicated all * copyright and related and neighboring rights to this software to the * public domain worldwide. This software is distributed without any * warranty. <http://creativecommons.org/publicdomain/zero/1.0/> */ #include "parg.h" #include <assert.h>#include <stdlib.h>#include <string.h> /* * Check if state is at end of argv. */static intis_argv_end(const struct parg_state *ps, int argc, char *const argv[]){	return ps->optind >= argc || argv[ps->optind] == NULL;} /* * Match nextchar against optstring. */static intmatch_short(struct parg_state *ps, int argc, char *const argv[],            const char *optstring){	const char *p = strchr(optstring, *ps->nextchar); 	if (p == NULL) {		ps->optopt = *ps->nextchar++;		return '?';	} 	/* If no option argument, return option */	if (p[1] != ':') {		return *ps->nextchar++;	} 	/* If more characters, return as option argument */	if (ps->nextchar[1] != '\0') {		ps->optarg = &ps->nextchar[1];		ps->nextchar = NULL;		return *p;	} 	/* If option argument is optional, return option */	if (p[2] == ':') {		return *ps->nextchar++;	} 	/* Option argument required, so return next argv element */	if (is_argv_end(ps, argc, argv)) {		ps->optopt = *ps->nextchar++;		return optstring[0] == ':' ? ':' : '?';	} 	ps->optarg = argv[ps->optind++];	ps->nextchar = NULL;	return *p;} /* * Match string at nextchar against longopts. */static intmatch_long(struct parg_state *ps, int argc, char *const argv[],           const char *optstring,           const struct parg_option *longopts, int *longindex){	size_t len;	int num_match = 0;	int match = -1;	int i; 	len = strcspn(ps->nextchar, "="); 	for (i = 0; longopts[i].name; ++i) {		if (strncmp(ps->nextchar, longopts[i].name, len) == 0) {			match = i;			num_match++;			/* Take if exact match */			if (longopts[i].name[len] == '\0') {				num_match = 1;				break;			}		}	} 	/* Return '?' on no or ambiguous match */	if (num_match != 1) {		ps->optopt = 0;		ps->nextchar = NULL;		return '?';	} 	assert(match != -1); 	if (longindex) {		*longindex = match;	} 	if (ps->nextchar[len] == '=') {		/* Option argument present, check if extraneous */		if (longopts[match].has_arg == PARG_NOARG) {			ps->optopt = longopts[match].flag ? 0 : longopts[match].val;			ps->nextchar = NULL;			return optstring[0] == ':' ? ':' : '?';		}		else {			ps->optarg = &ps->nextchar[len + 1];		}	}	else if (longopts[match].has_arg == PARG_REQARG) {		/* Option argument required, so return next argv element */		if (is_argv_end(ps, argc, argv)) {			ps->optopt = longopts[match].flag ? 0 : longopts[match].val;			ps->nextchar = NULL;			return optstring[0] == ':' ? ':' : '?';		} 		ps->optarg = argv[ps->optind++];	} 	ps->nextchar = NULL; 	if (longopts[match].flag != NULL) {		*longopts[match].flag = longopts[match].val;		return 0;	} 	return longopts[match].val;} voidparg_init(struct parg_state *ps){	ps->optarg = NULL;	ps->optind = 1;	ps->optopt = '?';	ps->nextchar = NULL;} intparg_getopt(struct parg_state *ps, int argc, char *const argv[],            const char *optstring){	return parg_getopt_long(ps, argc, argv, optstring, NULL, NULL);} intparg_getopt_long(struct parg_state *ps, int argc, char *const argv[],                 const char *optstring,                 const struct parg_option *longopts, int *longindex){	assert(ps != NULL);	assert(argv != NULL);	assert(optstring != NULL); 	ps->optarg = NULL; 	if (argc < 2) {		return -1;	} 	/* Advance to next element if needed */	if (ps->nextchar == NULL || *ps->nextchar == '\0') {		if (is_argv_end(ps, argc, argv)) {			return -1;		} 		ps->nextchar = argv[ps->optind++]; 		/* Check for nonoption element (including '-') */		if (ps->nextchar[0] != '-' || ps->nextchar[1] == '\0') {			ps->optarg = ps->nextchar;			ps->nextchar = NULL;			return 1;		} 		/* Check for '--' */		if (ps->nextchar[1] == '-') {			if (ps->nextchar[2] == '\0') {				ps->nextchar = NULL;				return -1;			} 			if (longopts != NULL) {				ps->nextchar += 2; 				return match_long(ps, argc, argv, optstring,				                  longopts, longindex);			}		} 		ps->nextchar++;	} 	/* Match nextchar */	return match_short(ps, argc, argv, optstring);} /* * Reverse elements of `v` from `i` to `j`. */static voidreverse(char *v[], int i, int j){	while (j - i > 1) {		char *tmp = v[i];		v[i] = v[j - 1];		v[j - 1] = tmp;		++i;		--j;	}} /* * Reorder elements of `argv` with no special cases. * * This function assumes there is no `--` element, and the last element * is not an option missing a required argument. * * The algorithm is described here: * http://hardtoc.com/2016/11/07/reordering-arguments.html */static intparg_reorder_simple(int argc, char *argv[],                    const char *optstring,                    const struct parg_option *longopts){	struct parg_state ps;	int change;	int l = 0;	int m = 0;	int r = 0; 	if (argc < 2) {		return argc;	} 	do {		int nextind;		int c; 		parg_init(&ps); 		nextind = ps.optind; 		/* Parse until end of argument */		do {			c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL);		} while (ps.nextchar != NULL && *ps.nextchar != '\0'); 		change = 0; 		do {			/* Find next non-option */			for (l = nextind; c != 1 && c != -1;) {				l = ps.optind; 				do {					c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL);				} while (ps.nextchar != NULL && *ps.nextchar != '\0');			} 			/* Find next option */			for (m = l; c == 1;) {				m = ps.optind; 				do {					c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL);				} while (ps.nextchar != NULL && *ps.nextchar != '\0');			} 			/* Find next non-option */			for (r = m; c != 1 && c != -1;) {				r = ps.optind; 				do {					c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL);				} while (ps.nextchar != NULL && *ps.nextchar != '\0');			} 			/* Find next option */			for (nextind = r; c == 1;) {				nextind = ps.optind; 				do {					c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL);				} while (ps.nextchar != NULL && *ps.nextchar != '\0');			} 			if (m < r) {				change = 1;				reverse(argv, l, m);				reverse(argv, m, r);				reverse(argv, l, r);			}		} while (c != -1);	} while (change != 0); 	return l + (r - m);} intparg_reorder(int argc, char *argv[],             const char *optstring,             const struct parg_option *longopts){	struct parg_state ps;	int lastind;	int optend;	int c; 	assert(argv != NULL);	assert(optstring != NULL); 	if (argc < 2) {		return argc;	} 	parg_init(&ps); 	/* Find end of normal arguments */	do {		lastind = ps.optind; 		c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL); 		/* Check for trailing option with error */		if ((c == '?' || c == ':') && is_argv_end(&ps, argc, argv)) {			lastind = ps.optind - 1;			break;		}	} while (c != -1); 	optend = parg_reorder_simple(lastind, argv, optstring, longopts); 	/* Rotate `--` or trailing option with error into position */	if (lastind < argc) {		reverse(argv, optend, lastind);		reverse(argv, optend, lastind + 1);		++optend;	} 	return optend;}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t