| //===-- Implementation of getopt ------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "src/unistd/getopt.h" |
| #include "src/__support/CPP/optional.h" |
| #include "src/__support/CPP/string_view.h" |
| #include "src/__support/File/file.h" |
| #include "src/__support/common.h" |
| #include "src/stdio/fprintf.h" |
| |
| #include <stdio.h> |
| |
| // This is POSIX compliant and does not support GNU extensions, mainly this is |
| // just the re-ordering of argv elements such that unknown arguments can be |
| // easily iterated over. |
| |
| namespace LIBC_NAMESPACE { |
| |
| template <typename T> struct RefWrapper { |
| RefWrapper() = delete; |
| constexpr RefWrapper(T *p) : ptr{p} {} |
| constexpr RefWrapper(const RefWrapper &) = default; |
| RefWrapper &operator=(const RefWrapper &) = default; |
| operator T &() { return *ptr; } |
| T &get() { return *ptr; } |
| T *ptr; |
| }; |
| |
| struct GetoptContext { |
| RefWrapper<char *> optarg; |
| RefWrapper<int> optind; |
| RefWrapper<int> optopt; |
| RefWrapper<unsigned> optpos; |
| |
| RefWrapper<int> opterr; |
| |
| FILE *errstream; |
| |
| GetoptContext &operator=(const GetoptContext &) = default; |
| |
| template <typename... Ts> void report_error(const char *fmt, Ts... ts) { |
| if (opterr) |
| LIBC_NAMESPACE::fprintf( |
| errstream ? errstream |
| : reinterpret_cast<FILE *>(LIBC_NAMESPACE::stderr), |
| fmt, ts...); |
| } |
| }; |
| |
| struct OptstringParser { |
| using value_type = struct { |
| char c; |
| bool arg; |
| }; |
| |
| cpp::string_view optstring; |
| |
| struct iterator { |
| cpp::string_view curr; |
| |
| iterator operator++() { |
| curr = curr.substr(1); |
| return *this; |
| } |
| |
| bool operator!=(iterator other) { return curr.data() != other.curr.data(); } |
| |
| value_type operator*() { |
| value_type r{curr.front(), false}; |
| if (!curr.substr(1).empty() && curr.substr(1).front() == ':') { |
| this->operator++(); |
| r.arg = true; |
| } |
| return r; |
| } |
| }; |
| |
| iterator begin() { |
| bool skip = optstring.front() == '-' || optstring.front() == '+' || |
| optstring.front() == ':'; |
| return {optstring.substr(!!skip)}; |
| } |
| |
| iterator end() { return {optstring.substr(optstring.size())}; } |
| }; |
| |
| int getopt_r(int argc, char *const argv[], const char *optstring, |
| GetoptContext &ctx) { |
| auto failure = [&ctx](int ret = -1) { |
| ctx.optpos.get() = 0; |
| return ret; |
| }; |
| |
| if (ctx.optind >= argc || !argv[ctx.optind]) |
| return failure(); |
| |
| cpp::string_view current = |
| cpp::string_view{argv[ctx.optind]}.substr(ctx.optpos); |
| |
| auto move_forward = [¤t, &ctx] { |
| current = current.substr(1); |
| ctx.optpos.get()++; |
| }; |
| |
| // If optpos is nonzero, then we are already parsing a valid flag and these |
| // need not be checked. |
| if (ctx.optpos == 0) { |
| if (current[0] != '-') |
| return failure(); |
| |
| if (current == "--") { |
| ctx.optind.get()++; |
| return failure(); |
| } |
| |
| // Eat the '-' char. |
| move_forward(); |
| if (current.empty()) |
| return failure(); |
| } |
| |
| auto find_match = |
| [current, optstring]() -> cpp::optional<OptstringParser::value_type> { |
| for (auto i : OptstringParser{optstring}) |
| if (i.c == current[0]) |
| return i; |
| return {}; |
| }; |
| |
| auto match = find_match(); |
| if (!match) { |
| ctx.report_error("%s: illegal option -- %c\n", argv[0], current[0]); |
| ctx.optopt.get() = current[0]; |
| return failure('?'); |
| } |
| |
| // We've matched so eat that character. |
| move_forward(); |
| if (match->arg) { |
| // If we found an option that takes an argument and our current is not over, |
| // the rest of current is that argument. Ie, "-cabc" with opstring "c:", |
| // then optarg should point to "abc". Otherwise the argument to c will be in |
| // the next arg like "-c abc". |
| if (!current.empty()) { |
| // This const cast is fine because current was already holding a mutable |
| // string, it just doesn't have the semantics to note that, we could use |
| // span but it doesn't have string_view string niceties. |
| ctx.optarg.get() = const_cast<char *>(current.data()); |
| } else { |
| // One char lookahead to see if we ran out of arguments. If so, return ':' |
| // if the first character of optstring is ':'. optind must stay at the |
| // current value so only increase it after we known there is another arg. |
| if (ctx.optind + 1 >= argc || !argv[ctx.optind + 1]) { |
| ctx.report_error("%s: option requires an argument -- %c\n", argv[0], |
| match->c); |
| return failure(optstring[0] == ':' ? ':' : '?'); |
| } |
| ctx.optarg.get() = argv[++ctx.optind]; |
| } |
| ctx.optind++; |
| ctx.optpos.get() = 0; |
| } else if (current.empty()) { |
| // If this argument is now empty we are safe to move onto the next one. |
| ctx.optind++; |
| ctx.optpos.get() = 0; |
| } |
| |
| return match->c; |
| } |
| |
| namespace impl { |
| |
| extern "C" { |
| char *optarg = nullptr; |
| int optind = 1; |
| int optopt = 0; |
| int opterr = 0; |
| } |
| |
| static unsigned optpos; |
| |
| static GetoptContext ctx{&impl::optarg, &impl::optind, &impl::optopt, |
| &optpos, &impl::opterr, /*errstream=*/nullptr}; |
| |
| #ifndef LIBC_COPT_PUBLIC_PACKAGING |
| // This is used exclusively in tests. |
| void set_getopt_state(char **optarg, int *optind, int *optopt, unsigned *optpos, |
| int *opterr, FILE *errstream) { |
| ctx = {optarg, optind, optopt, optpos, opterr, errstream}; |
| } |
| #endif |
| |
| } // namespace impl |
| |
| LLVM_LIBC_FUNCTION(int, getopt, |
| (int argc, char *const argv[], const char *optstring)) { |
| return getopt_r(argc, argv, optstring, impl::ctx); |
| } |
| |
| } // namespace LIBC_NAMESPACE |