aboutsummaryrefslogtreecommitdiff
path: root/lib/getopt.c
blob: 8b4515dc196750eddf5102adf0d9c9496bdaa23e (plain)
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// SPDX-License-Identifier: GPL-2.0-only
/*
 * getopt.c - a simple getopt(3) implementation. See getopt.h for explanation.
 *
 * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
 * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
 */

#define LOG_CATEGORY LOGC_CORE

#include <common.h>
#include <getopt.h>
#include <log.h>

void getopt_init_state(struct getopt_state *gs)
{
	gs->index = 1;
	gs->arg_index = 1;
}

int __getopt(struct getopt_state *gs, int argc, char *const argv[],
	     const char *optstring, bool silent)
{
	char curopt;   /* current option character */
	const char *curoptp; /* pointer to the current option in optstring */

	while (1) {
		log_debug("arg_index: %d index: %d\n", gs->arg_index,
			  gs->index);

		/* `--` indicates the end of options */
		if (gs->arg_index == 1 && argv[gs->index] &&
		    !strcmp(argv[gs->index], "--")) {
			gs->index++;
			return -1;
		}

		/* Out of arguments */
		if (gs->index >= argc)
			return -1;

		/* Can't parse non-options */
		if (*argv[gs->index] != '-')
			return -1;

		/* We have found an option */
		curopt = argv[gs->index][gs->arg_index];
		if (curopt)
			break;
		/*
		 * no more options in current argv[] element; try the next one
		 */
		gs->index++;
		gs->arg_index = 1;
	}

	/* look up current option in optstring */
	curoptp = strchr(optstring, curopt);

	if (!curoptp) {
		if (!silent)
			printf("%s: invalid option -- %c\n", argv[0], curopt);
		gs->opt = curopt;
		gs->arg_index++;
		return '?';
	}

	if (*(curoptp + 1) != ':') {
		/* option with no argument. Just return it */
		gs->arg = NULL;
		gs->arg_index++;
		return curopt;
	}

	if (*(curoptp + 1) && *(curoptp + 2) == ':') {
		/* optional argument */
		if (argv[gs->index][gs->arg_index + 1]) {
			/* optional argument with directly following arg */
			gs->arg = argv[gs->index++] + gs->arg_index + 1;
			gs->arg_index = 1;
			return curopt;
		}
		if (gs->index + 1 == argc) {
			/* We are at the last argv[] element */
			gs->arg = NULL;
			gs->index++;
			return curopt;
		}
		if (*argv[gs->index + 1] != '-') {
			/*
			 * optional argument with arg in next argv[] element
			 */
			gs->index++;
			gs->arg = argv[gs->index++];
			gs->arg_index = 1;
			return curopt;
		}

		/* no optional argument found */
		gs->arg = NULL;
		gs->arg_index = 1;
		gs->index++;
		return curopt;
	}

	if (argv[gs->index][gs->arg_index + 1]) {
		/* required argument with directly following arg */
		gs->arg = argv[gs->index++] + gs->arg_index + 1;
		gs->arg_index = 1;
		return curopt;
	}

	gs->index++;
	gs->arg_index = 1;

	if (gs->index >= argc || argv[gs->index][0] == '-') {
		if (!silent)
			printf("option requires an argument -- %c\n", curopt);
		gs->opt = curopt;
		return ':';
	}

	gs->arg = argv[gs->index++];
	return curopt;
}