brintos

brintos / llvm-project-archived public Read only

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