1 //===-- Implementation of getopt ------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "src/unistd/getopt.h"
10 #include "src/__support/CPP/optional.h"
11 #include "src/__support/CPP/string_view.h"
12 #include "src/__support/File/file.h"
13 #include "src/__support/common.h"
14 #include "src/stdio/fprintf.h"
18 // This is POSIX compliant and does not support GNU extensions, mainly this is
19 // just the re-ordering of argv elements such that unknown arguments can be
20 // easily iterated over.
22 namespace __llvm_libc
{
24 template <typename T
> struct RefWrapper
{
25 RefWrapper(T
*ptr
) : ptr(ptr
) {}
26 RefWrapper
&operator=(const RefWrapper
&) = default;
27 operator T
&() { return *ptr
; }
28 T
&get() { return *ptr
; }
32 struct GetoptContext
{
33 RefWrapper
<char *> optarg
;
34 RefWrapper
<int> optind
;
35 RefWrapper
<int> optopt
;
36 RefWrapper
<unsigned> optpos
;
42 GetoptContext
&operator=(const GetoptContext
&) = default;
44 template <typename
... Ts
> void report_error(const char *fmt
, Ts
... ts
) {
46 __llvm_libc::fprintf(errstream
, fmt
, ts
...);
50 struct OptstringParser
{
51 using value_type
= struct {
56 cpp::string_view optstring
;
59 cpp::string_view curr
;
61 iterator
operator++() {
62 curr
= curr
.substr(1);
66 bool operator!=(iterator other
) { return curr
.data() != other
.curr
.data(); }
68 value_type
operator*() {
69 value_type r
{curr
.front(), false};
70 if (!curr
.substr(1).empty() && curr
.substr(1).front() == ':') {
79 bool skip
= optstring
.front() == '-' || optstring
.front() == '+' ||
80 optstring
.front() == ':';
81 return {optstring
.substr(!!skip
)};
84 iterator
end() { return {optstring
.substr(optstring
.size())}; }
87 int getopt_r(int argc
, char *const argv
[], const char *optstring
,
89 auto failure
= [&ctx
](int ret
= -1) {
94 if (ctx
.optind
>= argc
|| !argv
[ctx
.optind
])
97 cpp::string_view current
=
98 cpp::string_view
{argv
[ctx
.optind
]}.substr(ctx
.optpos
);
100 auto move_forward
= [¤t
, &ctx
] {
101 current
= current
.substr(1);
105 // If optpos is nonzero, then we are already parsing a valid flag and these
106 // need not be checked.
107 if (ctx
.optpos
== 0) {
108 if (current
[0] != '-')
111 if (current
== "--") {
123 [current
, optstring
]() -> cpp::optional
<OptstringParser::value_type
> {
124 for (auto i
: OptstringParser
{optstring
})
125 if (i
.c
== current
[0])
130 auto match
= find_match();
132 ctx
.report_error("%s: illegal option -- %c\n", argv
[0], current
[0]);
133 ctx
.optopt
.get() = current
[0];
137 // We've matched so eat that character.
140 // If we found an option that takes an argument and our current is not over,
141 // the rest of current is that argument. Ie, "-cabc" with opstring "c:",
142 // then optarg should point to "abc". Otherwise the argument to c will be in
143 // the next arg like "-c abc".
144 if (!current
.empty()) {
145 // This const cast is fine because current was already holding a mutable
146 // string, it just doesn't have the semantics to note that, we could use
147 // span but it doesn't have string_view string niceties.
148 ctx
.optarg
.get() = const_cast<char *>(current
.data());
150 // One char lookahead to see if we ran out of arguments. If so, return ':'
151 // if the first character of optstring is ':'. optind must stay at the
152 // current value so only increase it after we known there is another arg.
153 if (ctx
.optind
+ 1 >= argc
|| !argv
[ctx
.optind
+ 1]) {
154 ctx
.report_error("%s: option requires an argument -- %c\n", argv
[0],
156 return failure(optstring
[0] == ':' ? ':' : '?');
158 ctx
.optarg
.get() = argv
[++ctx
.optind
];
161 ctx
.optpos
.get() = 0;
162 } else if (current
.empty()) {
163 // If this argument is now empty we are safe to move onto the next one.
165 ctx
.optpos
.get() = 0;
175 char *optarg
= nullptr;
182 static unsigned optpos
;
184 static GetoptContext ctx
{
185 &impl::optarg
, &impl::optind
,
186 &impl::optopt
, &optpos
,
187 impl::opterr
, reinterpret_cast<FILE *>(__llvm_libc::stderr
)};
189 #ifndef LIBC_COPT_PUBLIC_PACKAGING
190 // This is used exclusively in tests.
191 void set_getopt_state(char **optarg
, int *optind
, int *optopt
, unsigned *optpos
,
192 int opterr
, FILE *errstream
) {
193 ctx
= {optarg
, optind
, optopt
, optpos
, opterr
, errstream
};
199 LLVM_LIBC_FUNCTION(int, getopt
,
200 (int argc
, char *const argv
[], const char *optstring
)) {
201 return getopt_r(argc
, argv
, optstring
, impl::ctx
);
204 } // namespace __llvm_libc