Skip to content

Commit 59c57c0

Browse files
committed
args: migrate GNU getopt to getopt_port
1 parent 44b1b46 commit 59c57c0

File tree

7 files changed

+330
-7
lines changed

7 files changed

+330
-7
lines changed

Makefile

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ LDLIBS += $(BUILDDIR)/libfmt.a $(BUILDDIR)/libtiny-process-library.a -lcufetch
5656
CXXFLAGS ?= -mtune=generic -march=native
5757
CXXFLAGS += -flto=auto -ffat-lto-objects -fvisibility-inlines-hidden -fvisibility=hidden -Iinclude -Iinclude/libcufetch -Iinclude/libs -std=c++20 $(VARS) -DVERSION=\"$(VERSION)\" -DLOCALEDIR=\"$(LOCALEDIR)\" -DICONPREFIX=\"$(ICONPREFIX)\"
5858

59-
all: genver fmt toml tpl json libcufetch $(TARGET)
59+
all: genver fmt toml tpl getopt-port json libcufetch $(TARGET)
6060

6161
libcufetch: fmt toml
6262
ifeq ($(wildcard $(BUILDDIR)/libcufetch.so),)
@@ -79,6 +79,11 @@ ifeq ($(wildcard $(BUILDDIR)/libtiny-process-library.a),)
7979
make -C src/libs/tiny-process-library BUILDDIR=$(BUILDDIR)
8080
endif
8181

82+
getopt-port:
83+
ifeq ($(wildcard $(BUILDDIR)/getopt.o),)
84+
make -C src/libs/getopt_port BUILDDIR=$(BUILDDIR)
85+
endif
86+
8287
json:
8388
ifeq ($(wildcard $(BUILDDIR)/json.o),)
8489
make -C src/libs/json BUILDDIR=$(BUILDDIR)
@@ -89,7 +94,7 @@ ifeq ($(wildcard include/version.h),)
8994
./scripts/generateVersion.sh
9095
endif
9196

92-
$(TARGET): genver fmt toml json tpl libcufetch $(OBJ)
97+
$(TARGET): genver fmt toml tpl getopt-port json libcufetch $(OBJ)
9398
mkdir -p $(BUILDDIR)
9499
sh ./scripts/generateVersion.sh
95100
$(CXX) -o $(BUILDDIR)/$(TARGET) $(OBJ) $(BUILDDIR)/*.o $(LDFLAGS) $(LDLIBS)

cufetchpm/Makefile

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ LDLIBS += $(BUILDDIR)/libfmt.a $(BUILDDIR)/libtiny-process-library.a
3030
CXXFLAGS ?= -mtune=generic -march=native
3131
CXXFLAGS += -fvisibility=hidden -I../include -I../include/libs/ -Iinclude -std=c++20 $(VARS) -DVERSION=\"$(VERSION)\"
3232

33-
all: fmt toml tpl $(TARGET)
33+
all: fmt toml getopt-port tpl $(TARGET)
3434

3535
fmt:
3636
ifeq ($(wildcard $(BUILDDIR)/libfmt.a),)
@@ -44,13 +44,19 @@ ifeq ($(wildcard $(BUILDDIR)/toml.o),)
4444
make -C ../src/libs/toml++ BUILDDIR=cufetchpm/$(BUILDDIR)
4545
endif
4646

47+
getopt-port:
48+
ifeq ($(wildcard $(BUILDDIR)/getopt.o),)
49+
mkdir -p $(BUILDDIR)
50+
make -C ../src/libs/getopt_port BUILDDIR=$(BUILDDIR)
51+
endif
52+
4753
tpl:
4854
ifeq ($(wildcard $(BUILDDIR)/libtiny-process-library.a),)
4955
mkdir -p $(BUILDDIR)
5056
make -C ../src/libs/tiny-process-library BUILDDIR=cufetchpm/$(BUILDDIR)
5157
endif
5258

53-
$(TARGET): fmt toml tpl $(OBJ)
59+
$(TARGET): fmt toml getopt-port tpl $(OBJ)
5460
mkdir -p $(BUILDDIR)
5561
$(CXX) $(OBJ) $(BUILDDIR)/*.o -o $(BUILDDIR)/$(TARGET) $(LDFLAGS) $(LDLIBS)
5662

cufetchpm/src/main.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#include <getopt.h>
2-
31
#include "pluginManager.hpp"
42
#include "stateManager.hpp"
53
#if (!__has_include("version.h"))
@@ -8,6 +6,8 @@
86
#include "version.h"
97
#endif
108

9+
#include "getopt_port/getopt.h"
10+
1111
static void version()
1212
{
1313
fmt::print(

include/libs/getopt_port/getopt.h

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2012-2023, Kim Grasman <kim.grasman@gmail.com>
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
* * Redistributions of source code must retain the above copyright
8+
* notice, this list of conditions and the following disclaimer.
9+
* * Redistributions in binary form must reproduce the above copyright
10+
* notice, this list of conditions and the following disclaimer in the
11+
* documentation and/or other materials provided with the distribution.
12+
* * Neither the name of Kim Grasman nor the
13+
* names of contributors may be used to endorse or promote products
14+
* derived from this software without specific prior written permission.
15+
*
16+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+
* ARE DISCLAIMED. IN NO EVENT SHALL KIM GRASMAN BE LIABLE FOR ANY
20+
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
*
27+
******************************************************************************/
28+
29+
#ifndef INCLUDED_GETOPT_PORT_H
30+
#define INCLUDED_GETOPT_PORT_H
31+
32+
#if defined(__cplusplus)
33+
extern "C" {
34+
#endif
35+
36+
#define no_argument 1
37+
#define required_argument 2
38+
#define optional_argument 3
39+
40+
extern char* optarg;
41+
extern int optind, opterr, optopt;
42+
43+
struct option {
44+
const char* name;
45+
int has_arg;
46+
int* flag;
47+
int val;
48+
};
49+
50+
int getopt(int argc, char* const argv[], const char* optstring);
51+
52+
int getopt_long(int argc, char* const argv[],
53+
const char* optstring, const struct option* longopts, int* longindex);
54+
55+
#if defined(__cplusplus)
56+
}
57+
#endif
58+
59+
#endif // INCLUDED_GETOPT_PORT_H

src/libs/getopt_port/Makefile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CC ?= cc
2+
SRC = getopt.c
3+
TARGET = getopt.o
4+
CFLAGS = -I../../../include/libs -flto=auto -ffat-lto-objects -fvisibility=hidden -fPIC
5+
6+
all: $(TARGET)
7+
8+
$(TARGET):
9+
$(CC) $(SRC) $(CFLAGS) -c -o ../../../$(BUILDDIR)/$@
10+
11+
clean:
12+
rm -rf ../../../$(BUILDDIR)/getopt_port/$(TARGET)
13+
14+
.PHONY: $(TARGET) clean all

src/libs/getopt_port/getopt.c

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2012-2023, Kim Grasman <kim.grasman@gmail.com>
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
* * Redistributions of source code must retain the above copyright
8+
* notice, this list of conditions and the following disclaimer.
9+
* * Redistributions in binary form must reproduce the above copyright
10+
* notice, this list of conditions and the following disclaimer in the
11+
* documentation and/or other materials provided with the distribution.
12+
* * Neither the name of Kim Grasman nor the
13+
* names of contributors may be used to endorse or promote products
14+
* derived from this software without specific prior written permission.
15+
*
16+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+
* ARE DISCLAIMED. IN NO EVENT SHALL KIM GRASMAN BE LIABLE FOR ANY
20+
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
*
27+
******************************************************************************/
28+
29+
#include "getopt.h"
30+
31+
#include <stddef.h>
32+
#include <string.h>
33+
34+
char* optarg;
35+
int optopt;
36+
/* The variable optind [...] shall be initialized to 1 by the system. */
37+
int optind = 1;
38+
int opterr;
39+
40+
static char* optcursor = NULL;
41+
42+
/* Implemented based on [1] and [2] for optional arguments.
43+
optopt is handled FreeBSD-style, per [3].
44+
Other GNU and FreeBSD extensions are purely accidental.
45+
46+
[1] http://pubs.opengroup.org/onlinepubs/000095399/functions/getopt.html
47+
[2] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html
48+
[3] http://www.freebsd.org/cgi/man.cgi?query=getopt&sektion=3&manpath=FreeBSD+9.0-RELEASE
49+
*/
50+
int getopt(int argc, char* const argv[], const char* optstring) {
51+
int optchar = -1;
52+
const char* optdecl = NULL;
53+
54+
optarg = NULL;
55+
opterr = 0;
56+
optopt = 0;
57+
58+
/* Unspecified, but we need it to avoid overrunning the argv bounds. */
59+
if (optind >= argc)
60+
goto no_more_optchars;
61+
62+
/* If, when getopt() is called argv[optind] is a null pointer, getopt()
63+
shall return -1 without changing optind. */
64+
if (argv[optind] == NULL)
65+
goto no_more_optchars;
66+
67+
/* If, when getopt() is called *argv[optind] is not the character '-',
68+
getopt() shall return -1 without changing optind. */
69+
if (*argv[optind] != '-')
70+
goto no_more_optchars;
71+
72+
/* If, when getopt() is called argv[optind] points to the string "-",
73+
getopt() shall return -1 without changing optind. */
74+
if (strcmp(argv[optind], "-") == 0)
75+
goto no_more_optchars;
76+
77+
/* If, when getopt() is called argv[optind] points to the string "--",
78+
getopt() shall return -1 after incrementing optind. */
79+
if (strcmp(argv[optind], "--") == 0) {
80+
++optind;
81+
goto no_more_optchars;
82+
}
83+
84+
if (optcursor == NULL || *optcursor == '\0')
85+
optcursor = argv[optind] + 1;
86+
87+
optchar = *optcursor;
88+
89+
/* FreeBSD: The variable optopt saves the last known option character
90+
returned by getopt(). */
91+
optopt = optchar;
92+
93+
/* The getopt() function shall return the next option character (if one is
94+
found) from argv that matches a character in optstring, if there is
95+
one that matches. */
96+
optdecl = strchr(optstring, optchar);
97+
if (optdecl) {
98+
/* [I]f a character is followed by a colon, the option takes an
99+
argument. */
100+
if (optdecl[1] == ':') {
101+
optarg = ++optcursor;
102+
if (*optarg == '\0') {
103+
/* GNU extension: Two colons mean an option takes an
104+
optional arg; if there is text in the current argv-element
105+
(i.e., in the same word as the option name itself, for example,
106+
"-oarg"), then it is returned in optarg, otherwise optarg is set
107+
to zero. */
108+
if (optdecl[2] != ':') {
109+
/* If the option was the last character in the string pointed to by
110+
an element of argv, then optarg shall contain the next element
111+
of argv, and optind shall be incremented by 2. If the resulting
112+
value of optind is greater than argc, this indicates a missing
113+
option-argument, and getopt() shall return an error indication.
114+
115+
Otherwise, optarg shall point to the string following the
116+
option character in that element of argv, and optind shall be
117+
incremented by 1.
118+
*/
119+
if (++optind < argc) {
120+
optarg = argv[optind];
121+
} else {
122+
/* If it detects a missing option-argument, it shall return the
123+
colon character ( ':' ) if the first character of optstring
124+
was a colon, or a question-mark character ( '?' ) otherwise.
125+
*/
126+
optarg = NULL;
127+
optchar = (optstring[0] == ':') ? ':' : '?';
128+
}
129+
} else {
130+
optarg = NULL;
131+
}
132+
}
133+
134+
optcursor = NULL;
135+
}
136+
} else {
137+
/* If getopt() encounters an option character that is not contained in
138+
optstring, it shall return the question-mark ( '?' ) character. */
139+
optchar = '?';
140+
}
141+
142+
if (optcursor == NULL || *++optcursor == '\0')
143+
++optind;
144+
145+
return optchar;
146+
147+
no_more_optchars:
148+
optcursor = NULL;
149+
return -1;
150+
}
151+
152+
/* Implementation based on [1].
153+
154+
[1] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html
155+
*/
156+
int getopt_long(int argc, char* const argv[], const char* optstring,
157+
const struct option* longopts, int* longindex) {
158+
const struct option* o = longopts;
159+
const struct option* match = NULL;
160+
int num_matches = 0;
161+
size_t argument_name_length = 0;
162+
size_t option_length = 0;
163+
const char* current_argument = NULL;
164+
int retval = -1;
165+
166+
optarg = NULL;
167+
optopt = 0;
168+
169+
if (optind >= argc)
170+
return -1;
171+
172+
if (strlen(argv[optind]) < 3 || strncmp(argv[optind], "--", 2) != 0)
173+
return getopt(argc, argv, optstring);
174+
175+
/* It's an option; starts with -- and is longer than two chars. */
176+
current_argument = argv[optind] + 2;
177+
argument_name_length = strcspn(current_argument, "=");
178+
for (; o->name; ++o) {
179+
/* Check for exact match first. */
180+
option_length = strlen(o->name);
181+
if (option_length == argument_name_length &&
182+
strncmp(o->name, current_argument, option_length) == 0) {
183+
match = o;
184+
num_matches = 1;
185+
break;
186+
}
187+
188+
/* If not exact, count the number of abbreviated matches. */
189+
if (strncmp(o->name, current_argument, argument_name_length) == 0) {
190+
match = o;
191+
++num_matches;
192+
}
193+
}
194+
195+
if (num_matches == 1) {
196+
/* If longindex is not NULL, it points to a variable which is set to the
197+
index of the long option relative to longopts. */
198+
if (longindex)
199+
*longindex = (int)(match - longopts);
200+
201+
/* If flag is NULL, then getopt_long() shall return val.
202+
Otherwise, getopt_long() returns 0, and flag shall point to a variable
203+
which shall be set to val if the option is found, but left unchanged if
204+
the option is not found. */
205+
if (match->flag)
206+
*(match->flag) = match->val;
207+
208+
retval = match->flag ? 0 : match->val;
209+
210+
if (match->has_arg != no_argument) {
211+
optarg = strchr(argv[optind], '=');
212+
if (optarg != NULL)
213+
++optarg;
214+
215+
if (match->has_arg == required_argument) {
216+
/* Only scan the next argv for required arguments. Behavior is not
217+
specified, but has been observed with Ubuntu and Mac OSX. */
218+
if (optarg == NULL && ++optind < argc) {
219+
optarg = argv[optind];
220+
}
221+
222+
if (optarg == NULL)
223+
retval = ':';
224+
}
225+
} else if (strchr(argv[optind], '=')) {
226+
/* An argument was provided to a non-argument option.
227+
I haven't seen this specified explicitly, but both GNU and BSD-based
228+
implementations show this behavior.
229+
*/
230+
retval = '?';
231+
}
232+
} else {
233+
/* Unknown option or ambiguous match. */
234+
retval = '?';
235+
}
236+
237+
++optind;
238+
return retval;
239+
}

0 commit comments

Comments
 (0)