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 <common.h>
12 #include <getopt.h>
13 #include <log.h>
14 
getopt_init_state(struct getopt_state * gs)15 void getopt_init_state(struct getopt_state *gs)
16 {
17 	gs->index = 1;
18 	gs->arg_index = 1;
19 }
20 
__getopt(struct getopt_state * gs,int argc,char * const argv[],const char * optstring,bool silent)21 int __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