xref: /aosp_15_r20/external/llvm-libc/src/unistd/getopt.cpp (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
1*71db0c75SAndroid Build Coastguard Worker //===-- Implementation of getopt ------------------------------------------===//
2*71db0c75SAndroid Build Coastguard Worker //
3*71db0c75SAndroid Build Coastguard Worker // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*71db0c75SAndroid Build Coastguard Worker // See https://llvm.org/LICENSE.txt for license information.
5*71db0c75SAndroid Build Coastguard Worker // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*71db0c75SAndroid Build Coastguard Worker //
7*71db0c75SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
8*71db0c75SAndroid Build Coastguard Worker 
9*71db0c75SAndroid Build Coastguard Worker #include "src/unistd/getopt.h"
10*71db0c75SAndroid Build Coastguard Worker #include "src/__support/CPP/optional.h"
11*71db0c75SAndroid Build Coastguard Worker #include "src/__support/CPP/string_view.h"
12*71db0c75SAndroid Build Coastguard Worker #include "src/__support/File/file.h"
13*71db0c75SAndroid Build Coastguard Worker #include "src/__support/common.h"
14*71db0c75SAndroid Build Coastguard Worker #include "src/__support/macros/config.h"
15*71db0c75SAndroid Build Coastguard Worker #include "src/stdio/fprintf.h"
16*71db0c75SAndroid Build Coastguard Worker 
17*71db0c75SAndroid Build Coastguard Worker #include "hdr/types/FILE.h"
18*71db0c75SAndroid Build Coastguard Worker 
19*71db0c75SAndroid Build Coastguard Worker // This is POSIX compliant and does not support GNU extensions, mainly this is
20*71db0c75SAndroid Build Coastguard Worker // just the re-ordering of argv elements such that unknown arguments can be
21*71db0c75SAndroid Build Coastguard Worker // easily iterated over.
22*71db0c75SAndroid Build Coastguard Worker 
23*71db0c75SAndroid Build Coastguard Worker namespace LIBC_NAMESPACE_DECL {
24*71db0c75SAndroid Build Coastguard Worker 
25*71db0c75SAndroid Build Coastguard Worker template <typename T> struct RefWrapper {
26*71db0c75SAndroid Build Coastguard Worker   RefWrapper() = delete;
RefWrapperLIBC_NAMESPACE_DECL::RefWrapper27*71db0c75SAndroid Build Coastguard Worker   constexpr RefWrapper(T *p) : ptr{p} {}
28*71db0c75SAndroid Build Coastguard Worker   constexpr RefWrapper(const RefWrapper &) = default;
29*71db0c75SAndroid Build Coastguard Worker   RefWrapper &operator=(const RefWrapper &) = default;
operator T&LIBC_NAMESPACE_DECL::RefWrapper30*71db0c75SAndroid Build Coastguard Worker   operator T &() { return *ptr; }
getLIBC_NAMESPACE_DECL::RefWrapper31*71db0c75SAndroid Build Coastguard Worker   T &get() { return *ptr; }
32*71db0c75SAndroid Build Coastguard Worker   T *ptr;
33*71db0c75SAndroid Build Coastguard Worker };
34*71db0c75SAndroid Build Coastguard Worker 
35*71db0c75SAndroid Build Coastguard Worker struct GetoptContext {
36*71db0c75SAndroid Build Coastguard Worker   RefWrapper<char *> optarg;
37*71db0c75SAndroid Build Coastguard Worker   RefWrapper<int> optind;
38*71db0c75SAndroid Build Coastguard Worker   RefWrapper<int> optopt;
39*71db0c75SAndroid Build Coastguard Worker   RefWrapper<unsigned> optpos;
40*71db0c75SAndroid Build Coastguard Worker 
41*71db0c75SAndroid Build Coastguard Worker   RefWrapper<int> opterr;
42*71db0c75SAndroid Build Coastguard Worker 
43*71db0c75SAndroid Build Coastguard Worker   FILE *errstream;
44*71db0c75SAndroid Build Coastguard Worker 
45*71db0c75SAndroid Build Coastguard Worker   GetoptContext &operator=(const GetoptContext &) = default;
46*71db0c75SAndroid Build Coastguard Worker 
report_errorLIBC_NAMESPACE_DECL::GetoptContext47*71db0c75SAndroid Build Coastguard Worker   template <typename... Ts> void report_error(const char *fmt, Ts... ts) {
48*71db0c75SAndroid Build Coastguard Worker     if (opterr)
49*71db0c75SAndroid Build Coastguard Worker       LIBC_NAMESPACE::fprintf(
50*71db0c75SAndroid Build Coastguard Worker           errstream ? errstream
51*71db0c75SAndroid Build Coastguard Worker                     : reinterpret_cast<FILE *>(LIBC_NAMESPACE::stderr),
52*71db0c75SAndroid Build Coastguard Worker           fmt, ts...);
53*71db0c75SAndroid Build Coastguard Worker   }
54*71db0c75SAndroid Build Coastguard Worker };
55*71db0c75SAndroid Build Coastguard Worker 
56*71db0c75SAndroid Build Coastguard Worker struct OptstringParser {
57*71db0c75SAndroid Build Coastguard Worker   using value_type = struct {
58*71db0c75SAndroid Build Coastguard Worker     char c;
59*71db0c75SAndroid Build Coastguard Worker     bool arg;
60*71db0c75SAndroid Build Coastguard Worker   };
61*71db0c75SAndroid Build Coastguard Worker 
62*71db0c75SAndroid Build Coastguard Worker   cpp::string_view optstring;
63*71db0c75SAndroid Build Coastguard Worker 
64*71db0c75SAndroid Build Coastguard Worker   struct iterator {
65*71db0c75SAndroid Build Coastguard Worker     cpp::string_view curr;
66*71db0c75SAndroid Build Coastguard Worker 
operator ++LIBC_NAMESPACE_DECL::OptstringParser::iterator67*71db0c75SAndroid Build Coastguard Worker     iterator operator++() {
68*71db0c75SAndroid Build Coastguard Worker       curr = curr.substr(1);
69*71db0c75SAndroid Build Coastguard Worker       return *this;
70*71db0c75SAndroid Build Coastguard Worker     }
71*71db0c75SAndroid Build Coastguard Worker 
operator !=LIBC_NAMESPACE_DECL::OptstringParser::iterator72*71db0c75SAndroid Build Coastguard Worker     bool operator!=(iterator other) { return curr.data() != other.curr.data(); }
73*71db0c75SAndroid Build Coastguard Worker 
operator *LIBC_NAMESPACE_DECL::OptstringParser::iterator74*71db0c75SAndroid Build Coastguard Worker     value_type operator*() {
75*71db0c75SAndroid Build Coastguard Worker       value_type r{curr.front(), false};
76*71db0c75SAndroid Build Coastguard Worker       if (!curr.substr(1).empty() && curr.substr(1).front() == ':') {
77*71db0c75SAndroid Build Coastguard Worker         this->operator++();
78*71db0c75SAndroid Build Coastguard Worker         r.arg = true;
79*71db0c75SAndroid Build Coastguard Worker       }
80*71db0c75SAndroid Build Coastguard Worker       return r;
81*71db0c75SAndroid Build Coastguard Worker     }
82*71db0c75SAndroid Build Coastguard Worker   };
83*71db0c75SAndroid Build Coastguard Worker 
beginLIBC_NAMESPACE_DECL::OptstringParser84*71db0c75SAndroid Build Coastguard Worker   iterator begin() {
85*71db0c75SAndroid Build Coastguard Worker     bool skip = optstring.front() == '-' || optstring.front() == '+' ||
86*71db0c75SAndroid Build Coastguard Worker                 optstring.front() == ':';
87*71db0c75SAndroid Build Coastguard Worker     return {optstring.substr(!!skip)};
88*71db0c75SAndroid Build Coastguard Worker   }
89*71db0c75SAndroid Build Coastguard Worker 
endLIBC_NAMESPACE_DECL::OptstringParser90*71db0c75SAndroid Build Coastguard Worker   iterator end() { return {optstring.substr(optstring.size())}; }
91*71db0c75SAndroid Build Coastguard Worker };
92*71db0c75SAndroid Build Coastguard Worker 
getopt_r(int argc,char * const argv[],const char * optstring,GetoptContext & ctx)93*71db0c75SAndroid Build Coastguard Worker int getopt_r(int argc, char *const argv[], const char *optstring,
94*71db0c75SAndroid Build Coastguard Worker              GetoptContext &ctx) {
95*71db0c75SAndroid Build Coastguard Worker   auto failure = [&ctx](int ret = -1) {
96*71db0c75SAndroid Build Coastguard Worker     ctx.optpos.get() = 0;
97*71db0c75SAndroid Build Coastguard Worker     return ret;
98*71db0c75SAndroid Build Coastguard Worker   };
99*71db0c75SAndroid Build Coastguard Worker 
100*71db0c75SAndroid Build Coastguard Worker   if (ctx.optind >= argc || !argv[ctx.optind])
101*71db0c75SAndroid Build Coastguard Worker     return failure();
102*71db0c75SAndroid Build Coastguard Worker 
103*71db0c75SAndroid Build Coastguard Worker   cpp::string_view current =
104*71db0c75SAndroid Build Coastguard Worker       cpp::string_view{argv[ctx.optind]}.substr(ctx.optpos);
105*71db0c75SAndroid Build Coastguard Worker 
106*71db0c75SAndroid Build Coastguard Worker   auto move_forward = [&current, &ctx] {
107*71db0c75SAndroid Build Coastguard Worker     current = current.substr(1);
108*71db0c75SAndroid Build Coastguard Worker     ctx.optpos.get()++;
109*71db0c75SAndroid Build Coastguard Worker   };
110*71db0c75SAndroid Build Coastguard Worker 
111*71db0c75SAndroid Build Coastguard Worker   // If optpos is nonzero, then we are already parsing a valid flag and these
112*71db0c75SAndroid Build Coastguard Worker   // need not be checked.
113*71db0c75SAndroid Build Coastguard Worker   if (ctx.optpos == 0) {
114*71db0c75SAndroid Build Coastguard Worker     if (current[0] != '-')
115*71db0c75SAndroid Build Coastguard Worker       return failure();
116*71db0c75SAndroid Build Coastguard Worker 
117*71db0c75SAndroid Build Coastguard Worker     if (current == "--") {
118*71db0c75SAndroid Build Coastguard Worker       ctx.optind.get()++;
119*71db0c75SAndroid Build Coastguard Worker       return failure();
120*71db0c75SAndroid Build Coastguard Worker     }
121*71db0c75SAndroid Build Coastguard Worker 
122*71db0c75SAndroid Build Coastguard Worker     // Eat the '-' char.
123*71db0c75SAndroid Build Coastguard Worker     move_forward();
124*71db0c75SAndroid Build Coastguard Worker     if (current.empty())
125*71db0c75SAndroid Build Coastguard Worker       return failure();
126*71db0c75SAndroid Build Coastguard Worker   }
127*71db0c75SAndroid Build Coastguard Worker 
128*71db0c75SAndroid Build Coastguard Worker   auto find_match =
129*71db0c75SAndroid Build Coastguard Worker       [current, optstring]() -> cpp::optional<OptstringParser::value_type> {
130*71db0c75SAndroid Build Coastguard Worker     for (auto i : OptstringParser{optstring})
131*71db0c75SAndroid Build Coastguard Worker       if (i.c == current[0])
132*71db0c75SAndroid Build Coastguard Worker         return i;
133*71db0c75SAndroid Build Coastguard Worker     return {};
134*71db0c75SAndroid Build Coastguard Worker   };
135*71db0c75SAndroid Build Coastguard Worker 
136*71db0c75SAndroid Build Coastguard Worker   auto match = find_match();
137*71db0c75SAndroid Build Coastguard Worker   if (!match) {
138*71db0c75SAndroid Build Coastguard Worker     ctx.report_error("%s: illegal option -- %c\n", argv[0], current[0]);
139*71db0c75SAndroid Build Coastguard Worker     ctx.optopt.get() = current[0];
140*71db0c75SAndroid Build Coastguard Worker     return failure('?');
141*71db0c75SAndroid Build Coastguard Worker   }
142*71db0c75SAndroid Build Coastguard Worker 
143*71db0c75SAndroid Build Coastguard Worker   // We've matched so eat that character.
144*71db0c75SAndroid Build Coastguard Worker   move_forward();
145*71db0c75SAndroid Build Coastguard Worker   if (match->arg) {
146*71db0c75SAndroid Build Coastguard Worker     // If we found an option that takes an argument and our current is not over,
147*71db0c75SAndroid Build Coastguard Worker     // the rest of current is that argument. Ie, "-cabc" with opstring "c:",
148*71db0c75SAndroid Build Coastguard Worker     // then optarg should point to "abc". Otherwise the argument to c will be in
149*71db0c75SAndroid Build Coastguard Worker     // the next arg like "-c abc".
150*71db0c75SAndroid Build Coastguard Worker     if (!current.empty()) {
151*71db0c75SAndroid Build Coastguard Worker       // This const cast is fine because current was already holding a mutable
152*71db0c75SAndroid Build Coastguard Worker       // string, it just doesn't have the semantics to note that, we could use
153*71db0c75SAndroid Build Coastguard Worker       // span but it doesn't have string_view string niceties.
154*71db0c75SAndroid Build Coastguard Worker       ctx.optarg.get() = const_cast<char *>(current.data());
155*71db0c75SAndroid Build Coastguard Worker     } else {
156*71db0c75SAndroid Build Coastguard Worker       // One char lookahead to see if we ran out of arguments. If so, return ':'
157*71db0c75SAndroid Build Coastguard Worker       // if the first character of optstring is ':'. optind must stay at the
158*71db0c75SAndroid Build Coastguard Worker       // current value so only increase it after we known there is another arg.
159*71db0c75SAndroid Build Coastguard Worker       if (ctx.optind + 1 >= argc || !argv[ctx.optind + 1]) {
160*71db0c75SAndroid Build Coastguard Worker         ctx.report_error("%s: option requires an argument -- %c\n", argv[0],
161*71db0c75SAndroid Build Coastguard Worker                          match->c);
162*71db0c75SAndroid Build Coastguard Worker         return failure(optstring[0] == ':' ? ':' : '?');
163*71db0c75SAndroid Build Coastguard Worker       }
164*71db0c75SAndroid Build Coastguard Worker       ctx.optarg.get() = argv[++ctx.optind];
165*71db0c75SAndroid Build Coastguard Worker     }
166*71db0c75SAndroid Build Coastguard Worker     ctx.optind++;
167*71db0c75SAndroid Build Coastguard Worker     ctx.optpos.get() = 0;
168*71db0c75SAndroid Build Coastguard Worker   } else if (current.empty()) {
169*71db0c75SAndroid Build Coastguard Worker     // If this argument is now empty we are safe to move onto the next one.
170*71db0c75SAndroid Build Coastguard Worker     ctx.optind++;
171*71db0c75SAndroid Build Coastguard Worker     ctx.optpos.get() = 0;
172*71db0c75SAndroid Build Coastguard Worker   }
173*71db0c75SAndroid Build Coastguard Worker 
174*71db0c75SAndroid Build Coastguard Worker   return match->c;
175*71db0c75SAndroid Build Coastguard Worker }
176*71db0c75SAndroid Build Coastguard Worker 
177*71db0c75SAndroid Build Coastguard Worker namespace impl {
178*71db0c75SAndroid Build Coastguard Worker 
179*71db0c75SAndroid Build Coastguard Worker extern "C" {
180*71db0c75SAndroid Build Coastguard Worker char *optarg = nullptr;
181*71db0c75SAndroid Build Coastguard Worker int optind = 1;
182*71db0c75SAndroid Build Coastguard Worker int optopt = 0;
183*71db0c75SAndroid Build Coastguard Worker int opterr = 0;
184*71db0c75SAndroid Build Coastguard Worker }
185*71db0c75SAndroid Build Coastguard Worker 
186*71db0c75SAndroid Build Coastguard Worker static unsigned optpos;
187*71db0c75SAndroid Build Coastguard Worker 
188*71db0c75SAndroid Build Coastguard Worker static GetoptContext ctx{&impl::optarg, &impl::optind, &impl::optopt,
189*71db0c75SAndroid Build Coastguard Worker                          &optpos,       &impl::opterr, /*errstream=*/nullptr};
190*71db0c75SAndroid Build Coastguard Worker 
191*71db0c75SAndroid Build Coastguard Worker #ifndef LIBC_COPT_PUBLIC_PACKAGING
192*71db0c75SAndroid Build Coastguard Worker // This is used exclusively in tests.
set_getopt_state(char ** optarg,int * optind,int * optopt,unsigned * optpos,int * opterr,FILE * errstream)193*71db0c75SAndroid Build Coastguard Worker void set_getopt_state(char **optarg, int *optind, int *optopt, unsigned *optpos,
194*71db0c75SAndroid Build Coastguard Worker                       int *opterr, FILE *errstream) {
195*71db0c75SAndroid Build Coastguard Worker   ctx = {optarg, optind, optopt, optpos, opterr, errstream};
196*71db0c75SAndroid Build Coastguard Worker }
197*71db0c75SAndroid Build Coastguard Worker #endif
198*71db0c75SAndroid Build Coastguard Worker 
199*71db0c75SAndroid Build Coastguard Worker } // namespace impl
200*71db0c75SAndroid Build Coastguard Worker 
201*71db0c75SAndroid Build Coastguard Worker LLVM_LIBC_FUNCTION(int, getopt,
202*71db0c75SAndroid Build Coastguard Worker                    (int argc, char *const argv[], const char *optstring)) {
203*71db0c75SAndroid Build Coastguard Worker   return getopt_r(argc, argv, optstring, impl::ctx);
204*71db0c75SAndroid Build Coastguard Worker }
205*71db0c75SAndroid Build Coastguard Worker 
206*71db0c75SAndroid Build Coastguard Worker } // namespace LIBC_NAMESPACE_DECL
207