~emersion/mrsh

ref: 448ee4f664f60d70d229af5462ef349240af8d7f mrsh/builtin/getopts.c -rw-r--r-- 2.5 KiB
448ee4f6 — Ben Brown Reset optind to 0 1 year, 10 months ago
                                                                                
625ff377 delthas
23791269 delthas
625ff377 delthas
625ff377 delthas
23791269 delthas
625ff377 delthas
448ee4f6 Ben Brown
23791269 delthas
625ff377 delthas
23791269 delthas
625ff377 delthas
23791269 delthas
625ff377 delthas
625ff377 delthas
23791269 delthas
625ff377 delthas
625ff377 delthas
625ff377 delthas
625ff377 delthas
625ff377 delthas
23791269 delthas
625ff377 delthas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <limits.h>
#include <mrsh/buffer.h>
#include <mrsh/shell.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "builtin.h"

static const char getopts_usage[] = "usage: getopts optstring name [arg...]\n";

int builtin_getopts(struct mrsh_state *state, int argc, char *argv[]) {
	optind = 0;
	if (getopt(argc, argv, ":") != -1) {
		fprintf(stderr, "getopts: unknown option -- %c\n", optopt);
		fprintf(stderr, getopts_usage);
		return EXIT_FAILURE;
	}
	if (optind + 2 < argc) {
		fprintf(stderr, getopts_usage);
		return EXIT_FAILURE;
	}

	int optc;
	char **optv;
	if (optind + 2 > argc) {
		optc = argc - optind - 2;
		optv = &argv[optind + 2];
	} else {
		optc = state->args->argc;
		optv = state->args->argv;
	}
	char *optstring = argv[optind];
	char *name = argv[optind + 1];

	const char *optind_str = mrsh_env_get(state, "OPTIND", NULL);
	if (optind_str == NULL) {
		fprintf(stderr, "getopts: OPTIND is not defined\n");
		return EXIT_FAILURE;
	}
	char *endptr;
	long optind_long = strtol(optind_str, &endptr, 10);
	if (endptr[0] != '\0' || optind_long <= 0 || optind_long > INT_MAX) {
		fprintf(stderr, "getopts: OPTIND is not a positive integer\n");
		return EXIT_FAILURE;
	}
	optind = (int)optind_long;

	optopt = 0;
	int opt = getopt(optc, optv, optstring);

	char optind_fmt[16];
	snprintf(optind_fmt, sizeof(optind_fmt), "%d", optind);
	mrsh_env_set(state, "OPTIND", optind_fmt, MRSH_VAR_ATTRIB_NONE);

	if (optopt != 0) {
		if (opt == ':') {
			char value[] = {(char)optopt, '\0'};
			mrsh_env_set(state, "OPTARG", value, MRSH_VAR_ATTRIB_NONE);
		} else if (optstring[0] != ':') {
			mrsh_env_unset(state, "OPTARG");
		} else {
			// either missing option-argument or unknown option character
			// in the former case, unset OPTARG
			// in the latter case, set OPTARG to optopt
			bool opt_exists = false;
			size_t len = strlen(optstring);
			for (size_t i = 0; i < len; ++i) {
				if (optstring[i] == optopt) {
					opt_exists = true;
					break;
				}
			}
			if (opt_exists) {
				mrsh_env_unset(state, "OPTARG");
			} else {
				char value[] = {(char)optopt, '\0'};
				mrsh_env_set(state, "OPTARG", value, MRSH_VAR_ATTRIB_NONE);
			}
		}
	} else if (optarg != NULL) {
		mrsh_env_set(state, "OPTARG", optarg, MRSH_VAR_ATTRIB_NONE);
	} else {
		mrsh_env_unset(state, "OPTARG");
	}

	char value[] = {opt == -1 ? (char)'?' : (char)opt, '\0'};
	mrsh_env_set(state, name, value, MRSH_VAR_ATTRIB_NONE);

	if (opt == -1) {
		return EXIT_FAILURE;
	}
	return EXIT_SUCCESS;
}