1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * getopt.c - a simple getopt(3) implementation. See getopt.h for explanation.
4 *
5 * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
6 * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
7 */
8
9#define LOG_CATEGORY LOGC_CORE
10
11#include <getopt.h>
12#include <log.h>
13#include <linux/string.h>
14
15void getopt_init_state(struct getopt_state *gs)
16{
17	gs->index = 1;
18	gs->arg_index = 1;
19}
20
21int __getopt(struct getopt_state *gs, int argc, char *const argv[],
22	     const char *optstring, bool silent)
23{
24	char curopt;   /* current option character */
25	const char *curoptp; /* pointer to the current option in optstring */
26
27	while (1) {
28		log_debug("arg_index: %d index: %d\n", gs->arg_index,
29			  gs->index);
30
31		/* `--` indicates the end of options */
32		if (gs->arg_index == 1 && argv[gs->index] &&
33		    !strcmp(argv[gs->index], "--")) {
34			gs->index++;
35			return -1;
36		}
37
38		/* Out of arguments */
39		if (gs->index >= argc)
40			return -1;
41
42		/* Can't parse non-options */
43		if (*argv[gs->index] != '-')
44			return -1;
45
46		/* We have found an option */
47		curopt = argv[gs->index][gs->arg_index];
48		if (curopt)
49			break;
50		/*
51		 * no more options in current argv[] element; try the next one
52		 */
53		gs->index++;
54		gs->arg_index = 1;
55	}
56
57	/* look up current option in optstring */
58	curoptp = strchr(optstring, curopt);
59
60	if (!curoptp) {
61		if (!silent)
62			printf("%s: invalid option -- %c\n", argv[0], curopt);
63		gs->opt = curopt;
64		gs->arg_index++;
65		return '?';
66	}
67
68	if (*(curoptp + 1) != ':') {
69		/* option with no argument. Just return it */
70		gs->arg = NULL;
71		gs->arg_index++;
72		return curopt;
73	}
74
75	if (*(curoptp + 1) && *(curoptp + 2) == ':') {
76		/* optional argument */
77		if (argv[gs->index][gs->arg_index + 1]) {
78			/* optional argument with directly following arg */
79			gs->arg = argv[gs->index++] + gs->arg_index + 1;
80			gs->arg_index = 1;
81			return curopt;
82		}
83		if (gs->index + 1 == argc) {
84			/* We are at the last argv[] element */
85			gs->arg = NULL;
86			gs->index++;
87			return curopt;
88		}
89		if (*argv[gs->index + 1] != '-') {
90			/*
91			 * optional argument with arg in next argv[] element
92			 */
93			gs->index++;
94			gs->arg = argv[gs->index++];
95			gs->arg_index = 1;
96			return curopt;
97		}
98
99		/* no optional argument found */
100		gs->arg = NULL;
101		gs->arg_index = 1;
102		gs->index++;
103		return curopt;
104	}
105
106	if (argv[gs->index][gs->arg_index + 1]) {
107		/* required argument with directly following arg */
108		gs->arg = argv[gs->index++] + gs->arg_index + 1;
109		gs->arg_index = 1;
110		return curopt;
111	}
112
113	gs->index++;
114	gs->arg_index = 1;
115
116	if (gs->index >= argc || argv[gs->index][0] == '-') {
117		if (!silent)
118			printf("option requires an argument -- %c\n", curopt);
119		gs->opt = curopt;
120		return ':';
121	}
122
123	gs->arg = argv[gs->index++];
124	return curopt;
125}
126