• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- Implementation of getopt ------------------------------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 
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"
15 
16 #include <stdio.h>
17 
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.
21 
22 namespace LIBC_NAMESPACE {
23 
24 template <typename T> struct RefWrapper {
25   RefWrapper() = delete;
RefWrapperLIBC_NAMESPACE::RefWrapper26   constexpr RefWrapper(T *p) : ptr{p} {}
27   constexpr RefWrapper(const RefWrapper &) = default;
28   RefWrapper &operator=(const RefWrapper &) = default;
operator T&LIBC_NAMESPACE::RefWrapper29   operator T &() { return *ptr; }
getLIBC_NAMESPACE::RefWrapper30   T &get() { return *ptr; }
31   T *ptr;
32 };
33 
34 struct GetoptContext {
35   RefWrapper<char *> optarg;
36   RefWrapper<int> optind;
37   RefWrapper<int> optopt;
38   RefWrapper<unsigned> optpos;
39 
40   RefWrapper<int> opterr;
41 
42   FILE *errstream;
43 
44   GetoptContext &operator=(const GetoptContext &) = default;
45 
report_errorLIBC_NAMESPACE::GetoptContext46   template <typename... Ts> void report_error(const char *fmt, Ts... ts) {
47     if (opterr)
48       LIBC_NAMESPACE::fprintf(
49           errstream ? errstream
50                     : reinterpret_cast<FILE *>(LIBC_NAMESPACE::stderr),
51           fmt, ts...);
52   }
53 };
54 
55 struct OptstringParser {
56   using value_type = struct {
57     char c;
58     bool arg;
59   };
60 
61   cpp::string_view optstring;
62 
63   struct iterator {
64     cpp::string_view curr;
65 
operator ++LIBC_NAMESPACE::OptstringParser::iterator66     iterator operator++() {
67       curr = curr.substr(1);
68       return *this;
69     }
70 
operator !=LIBC_NAMESPACE::OptstringParser::iterator71     bool operator!=(iterator other) { return curr.data() != other.curr.data(); }
72 
operator *LIBC_NAMESPACE::OptstringParser::iterator73     value_type operator*() {
74       value_type r{curr.front(), false};
75       if (!curr.substr(1).empty() && curr.substr(1).front() == ':') {
76         this->operator++();
77         r.arg = true;
78       }
79       return r;
80     }
81   };
82 
beginLIBC_NAMESPACE::OptstringParser83   iterator begin() {
84     bool skip = optstring.front() == '-' || optstring.front() == '+' ||
85                 optstring.front() == ':';
86     return {optstring.substr(!!skip)};
87   }
88 
endLIBC_NAMESPACE::OptstringParser89   iterator end() { return {optstring.substr(optstring.size())}; }
90 };
91 
getopt_r(int argc,char * const argv[],const char * optstring,GetoptContext & ctx)92 int getopt_r(int argc, char *const argv[], const char *optstring,
93              GetoptContext &ctx) {
94   auto failure = [&ctx](int ret = -1) {
95     ctx.optpos.get() = 0;
96     return ret;
97   };
98 
99   if (ctx.optind >= argc || !argv[ctx.optind])
100     return failure();
101 
102   cpp::string_view current =
103       cpp::string_view{argv[ctx.optind]}.substr(ctx.optpos);
104 
105   auto move_forward = [&current, &ctx] {
106     current = current.substr(1);
107     ctx.optpos.get()++;
108   };
109 
110   // If optpos is nonzero, then we are already parsing a valid flag and these
111   // need not be checked.
112   if (ctx.optpos == 0) {
113     if (current[0] != '-')
114       return failure();
115 
116     if (current == "--") {
117       ctx.optind.get()++;
118       return failure();
119     }
120 
121     // Eat the '-' char.
122     move_forward();
123     if (current.empty())
124       return failure();
125   }
126 
127   auto find_match =
128       [current, optstring]() -> cpp::optional<OptstringParser::value_type> {
129     for (auto i : OptstringParser{optstring})
130       if (i.c == current[0])
131         return i;
132     return {};
133   };
134 
135   auto match = find_match();
136   if (!match) {
137     ctx.report_error("%s: illegal option -- %c\n", argv[0], current[0]);
138     ctx.optopt.get() = current[0];
139     return failure('?');
140   }
141 
142   // We've matched so eat that character.
143   move_forward();
144   if (match->arg) {
145     // If we found an option that takes an argument and our current is not over,
146     // the rest of current is that argument. Ie, "-cabc" with opstring "c:",
147     // then optarg should point to "abc". Otherwise the argument to c will be in
148     // the next arg like "-c abc".
149     if (!current.empty()) {
150       // This const cast is fine because current was already holding a mutable
151       // string, it just doesn't have the semantics to note that, we could use
152       // span but it doesn't have string_view string niceties.
153       ctx.optarg.get() = const_cast<char *>(current.data());
154     } else {
155       // One char lookahead to see if we ran out of arguments. If so, return ':'
156       // if the first character of optstring is ':'. optind must stay at the
157       // current value so only increase it after we known there is another arg.
158       if (ctx.optind + 1 >= argc || !argv[ctx.optind + 1]) {
159         ctx.report_error("%s: option requires an argument -- %c\n", argv[0],
160                          match->c);
161         return failure(optstring[0] == ':' ? ':' : '?');
162       }
163       ctx.optarg.get() = argv[++ctx.optind];
164     }
165     ctx.optind++;
166     ctx.optpos.get() = 0;
167   } else if (current.empty()) {
168     // If this argument is now empty we are safe to move onto the next one.
169     ctx.optind++;
170     ctx.optpos.get() = 0;
171   }
172 
173   return match->c;
174 }
175 
176 namespace impl {
177 
178 extern "C" {
179 char *optarg = nullptr;
180 int optind = 1;
181 int optopt = 0;
182 int opterr = 0;
183 }
184 
185 static unsigned optpos;
186 
187 static GetoptContext ctx{&impl::optarg, &impl::optind, &impl::optopt,
188                          &optpos,       &impl::opterr, /*errstream=*/nullptr};
189 
190 #ifndef LIBC_COPT_PUBLIC_PACKAGING
191 // This is used exclusively in tests.
set_getopt_state(char ** optarg,int * optind,int * optopt,unsigned * optpos,int * opterr,FILE * errstream)192 void set_getopt_state(char **optarg, int *optind, int *optopt, unsigned *optpos,
193                       int *opterr, FILE *errstream) {
194   ctx = {optarg, optind, optopt, optpos, opterr, errstream};
195 }
196 #endif
197 
198 } // namespace impl
199 
200 LLVM_LIBC_FUNCTION(int, getopt,
201                    (int argc, char *const argv[], const char *optstring)) {
202   return getopt_r(argc, argv, optstring, impl::ctx);
203 }
204 
205 } // namespace LIBC_NAMESPACE
206