xref: /llvm-project/libc/src/unistd/getopt.cpp (revision 5ff3ff33ff930e4ec49da7910612d8a41eb068cb)
15fd03c81SAlex Brachet //===-- Implementation of getopt ------------------------------------------===//
25fd03c81SAlex Brachet //
35fd03c81SAlex Brachet // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45fd03c81SAlex Brachet // See https://llvm.org/LICENSE.txt for license information.
55fd03c81SAlex Brachet // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65fd03c81SAlex Brachet //
75fd03c81SAlex Brachet //===----------------------------------------------------------------------===//
85fd03c81SAlex Brachet 
95fd03c81SAlex Brachet #include "src/unistd/getopt.h"
105fd03c81SAlex Brachet #include "src/__support/CPP/optional.h"
115fd03c81SAlex Brachet #include "src/__support/CPP/string_view.h"
125fd03c81SAlex Brachet #include "src/__support/File/file.h"
135fd03c81SAlex Brachet #include "src/__support/common.h"
14*5ff3ff33SPetr Hosek #include "src/__support/macros/config.h"
155fd03c81SAlex Brachet #include "src/stdio/fprintf.h"
165fd03c81SAlex Brachet 
175aed6d67SMichael Jones #include "hdr/types/FILE.h"
185fd03c81SAlex Brachet 
195fd03c81SAlex Brachet // This is POSIX compliant and does not support GNU extensions, mainly this is
205fd03c81SAlex Brachet // just the re-ordering of argv elements such that unknown arguments can be
215fd03c81SAlex Brachet // easily iterated over.
225fd03c81SAlex Brachet 
23*5ff3ff33SPetr Hosek namespace LIBC_NAMESPACE_DECL {
245fd03c81SAlex Brachet 
255fd03c81SAlex Brachet template <typename T> struct RefWrapper {
260ed4b177SRoland McGrath   RefWrapper() = delete;
270ed4b177SRoland McGrath   constexpr RefWrapper(T *p) : ptr{p} {}
280ed4b177SRoland McGrath   constexpr RefWrapper(const RefWrapper &) = default;
295fd03c81SAlex Brachet   RefWrapper &operator=(const RefWrapper &) = default;
305fd03c81SAlex Brachet   operator T &() { return *ptr; }
315fd03c81SAlex Brachet   T &get() { return *ptr; }
325fd03c81SAlex Brachet   T *ptr;
335fd03c81SAlex Brachet };
345fd03c81SAlex Brachet 
355fd03c81SAlex Brachet struct GetoptContext {
365fd03c81SAlex Brachet   RefWrapper<char *> optarg;
375fd03c81SAlex Brachet   RefWrapper<int> optind;
385fd03c81SAlex Brachet   RefWrapper<int> optopt;
395fd03c81SAlex Brachet   RefWrapper<unsigned> optpos;
405fd03c81SAlex Brachet 
41ed7ade9cSJoseph Huber   RefWrapper<int> opterr;
425fd03c81SAlex Brachet 
435fd03c81SAlex Brachet   FILE *errstream;
445fd03c81SAlex Brachet 
455fd03c81SAlex Brachet   GetoptContext &operator=(const GetoptContext &) = default;
465fd03c81SAlex Brachet 
475fd03c81SAlex Brachet   template <typename... Ts> void report_error(const char *fmt, Ts... ts) {
485fd03c81SAlex Brachet     if (opterr)
49b6bc9d72SGuillaume Chatelet       LIBC_NAMESPACE::fprintf(
50b6bc9d72SGuillaume Chatelet           errstream ? errstream
51b6bc9d72SGuillaume Chatelet                     : reinterpret_cast<FILE *>(LIBC_NAMESPACE::stderr),
52ed7ade9cSJoseph Huber           fmt, ts...);
535fd03c81SAlex Brachet   }
545fd03c81SAlex Brachet };
555fd03c81SAlex Brachet 
565fd03c81SAlex Brachet struct OptstringParser {
575fd03c81SAlex Brachet   using value_type = struct {
585fd03c81SAlex Brachet     char c;
595fd03c81SAlex Brachet     bool arg;
605fd03c81SAlex Brachet   };
615fd03c81SAlex Brachet 
625fd03c81SAlex Brachet   cpp::string_view optstring;
635fd03c81SAlex Brachet 
645fd03c81SAlex Brachet   struct iterator {
655fd03c81SAlex Brachet     cpp::string_view curr;
665fd03c81SAlex Brachet 
675fd03c81SAlex Brachet     iterator operator++() {
685fd03c81SAlex Brachet       curr = curr.substr(1);
695fd03c81SAlex Brachet       return *this;
705fd03c81SAlex Brachet     }
715fd03c81SAlex Brachet 
725fd03c81SAlex Brachet     bool operator!=(iterator other) { return curr.data() != other.curr.data(); }
735fd03c81SAlex Brachet 
745fd03c81SAlex Brachet     value_type operator*() {
755fd03c81SAlex Brachet       value_type r{curr.front(), false};
765fd03c81SAlex Brachet       if (!curr.substr(1).empty() && curr.substr(1).front() == ':') {
775fd03c81SAlex Brachet         this->operator++();
785fd03c81SAlex Brachet         r.arg = true;
795fd03c81SAlex Brachet       }
805fd03c81SAlex Brachet       return r;
815fd03c81SAlex Brachet     }
825fd03c81SAlex Brachet   };
835fd03c81SAlex Brachet 
845fd03c81SAlex Brachet   iterator begin() {
855fd03c81SAlex Brachet     bool skip = optstring.front() == '-' || optstring.front() == '+' ||
865fd03c81SAlex Brachet                 optstring.front() == ':';
875fd03c81SAlex Brachet     return {optstring.substr(!!skip)};
885fd03c81SAlex Brachet   }
895fd03c81SAlex Brachet 
905fd03c81SAlex Brachet   iterator end() { return {optstring.substr(optstring.size())}; }
915fd03c81SAlex Brachet };
925fd03c81SAlex Brachet 
935fd03c81SAlex Brachet int getopt_r(int argc, char *const argv[], const char *optstring,
945fd03c81SAlex Brachet              GetoptContext &ctx) {
955fd03c81SAlex Brachet   auto failure = [&ctx](int ret = -1) {
965fd03c81SAlex Brachet     ctx.optpos.get() = 0;
975fd03c81SAlex Brachet     return ret;
985fd03c81SAlex Brachet   };
995fd03c81SAlex Brachet 
1005fd03c81SAlex Brachet   if (ctx.optind >= argc || !argv[ctx.optind])
1015fd03c81SAlex Brachet     return failure();
1025fd03c81SAlex Brachet 
1035fd03c81SAlex Brachet   cpp::string_view current =
1045fd03c81SAlex Brachet       cpp::string_view{argv[ctx.optind]}.substr(ctx.optpos);
1055fd03c81SAlex Brachet 
1065fd03c81SAlex Brachet   auto move_forward = [&current, &ctx] {
1075fd03c81SAlex Brachet     current = current.substr(1);
1085fd03c81SAlex Brachet     ctx.optpos.get()++;
1095fd03c81SAlex Brachet   };
1105fd03c81SAlex Brachet 
1115fd03c81SAlex Brachet   // If optpos is nonzero, then we are already parsing a valid flag and these
1125fd03c81SAlex Brachet   // need not be checked.
1135fd03c81SAlex Brachet   if (ctx.optpos == 0) {
1145fd03c81SAlex Brachet     if (current[0] != '-')
1155fd03c81SAlex Brachet       return failure();
1165fd03c81SAlex Brachet 
1175fd03c81SAlex Brachet     if (current == "--") {
1185fd03c81SAlex Brachet       ctx.optind.get()++;
1195fd03c81SAlex Brachet       return failure();
1205fd03c81SAlex Brachet     }
1215fd03c81SAlex Brachet 
1225fd03c81SAlex Brachet     // Eat the '-' char.
1235fd03c81SAlex Brachet     move_forward();
1245fd03c81SAlex Brachet     if (current.empty())
1255fd03c81SAlex Brachet       return failure();
1265fd03c81SAlex Brachet   }
1275fd03c81SAlex Brachet 
1285fd03c81SAlex Brachet   auto find_match =
1295fd03c81SAlex Brachet       [current, optstring]() -> cpp::optional<OptstringParser::value_type> {
1305fd03c81SAlex Brachet     for (auto i : OptstringParser{optstring})
1315fd03c81SAlex Brachet       if (i.c == current[0])
1325fd03c81SAlex Brachet         return i;
1335fd03c81SAlex Brachet     return {};
1345fd03c81SAlex Brachet   };
1355fd03c81SAlex Brachet 
1365fd03c81SAlex Brachet   auto match = find_match();
1375fd03c81SAlex Brachet   if (!match) {
1385fd03c81SAlex Brachet     ctx.report_error("%s: illegal option -- %c\n", argv[0], current[0]);
1395fd03c81SAlex Brachet     ctx.optopt.get() = current[0];
1405fd03c81SAlex Brachet     return failure('?');
1415fd03c81SAlex Brachet   }
1425fd03c81SAlex Brachet 
1435fd03c81SAlex Brachet   // We've matched so eat that character.
1445fd03c81SAlex Brachet   move_forward();
1455fd03c81SAlex Brachet   if (match->arg) {
1465fd03c81SAlex Brachet     // If we found an option that takes an argument and our current is not over,
1475fd03c81SAlex Brachet     // the rest of current is that argument. Ie, "-cabc" with opstring "c:",
1485fd03c81SAlex Brachet     // then optarg should point to "abc". Otherwise the argument to c will be in
1495fd03c81SAlex Brachet     // the next arg like "-c abc".
1505fd03c81SAlex Brachet     if (!current.empty()) {
1515fd03c81SAlex Brachet       // This const cast is fine because current was already holding a mutable
1525fd03c81SAlex Brachet       // string, it just doesn't have the semantics to note that, we could use
1535fd03c81SAlex Brachet       // span but it doesn't have string_view string niceties.
1545fd03c81SAlex Brachet       ctx.optarg.get() = const_cast<char *>(current.data());
1555fd03c81SAlex Brachet     } else {
1565fd03c81SAlex Brachet       // One char lookahead to see if we ran out of arguments. If so, return ':'
1575fd03c81SAlex Brachet       // if the first character of optstring is ':'. optind must stay at the
1585fd03c81SAlex Brachet       // current value so only increase it after we known there is another arg.
1595fd03c81SAlex Brachet       if (ctx.optind + 1 >= argc || !argv[ctx.optind + 1]) {
1605fd03c81SAlex Brachet         ctx.report_error("%s: option requires an argument -- %c\n", argv[0],
1615fd03c81SAlex Brachet                          match->c);
1625fd03c81SAlex Brachet         return failure(optstring[0] == ':' ? ':' : '?');
1635fd03c81SAlex Brachet       }
1645fd03c81SAlex Brachet       ctx.optarg.get() = argv[++ctx.optind];
1655fd03c81SAlex Brachet     }
1665fd03c81SAlex Brachet     ctx.optind++;
1675fd03c81SAlex Brachet     ctx.optpos.get() = 0;
1685fd03c81SAlex Brachet   } else if (current.empty()) {
1695fd03c81SAlex Brachet     // If this argument is now empty we are safe to move onto the next one.
1705fd03c81SAlex Brachet     ctx.optind++;
1715fd03c81SAlex Brachet     ctx.optpos.get() = 0;
1725fd03c81SAlex Brachet   }
1735fd03c81SAlex Brachet 
1745fd03c81SAlex Brachet   return match->c;
1755fd03c81SAlex Brachet }
1765fd03c81SAlex Brachet 
1775fd03c81SAlex Brachet namespace impl {
1785fd03c81SAlex Brachet 
179a439c4afSSiva Chandra Reddy extern "C" {
180a439c4afSSiva Chandra Reddy char *optarg = nullptr;
181a439c4afSSiva Chandra Reddy int optind = 1;
182a439c4afSSiva Chandra Reddy int optopt = 0;
183a439c4afSSiva Chandra Reddy int opterr = 0;
184a439c4afSSiva Chandra Reddy }
1855fd03c81SAlex Brachet 
1865fd03c81SAlex Brachet static unsigned optpos;
1875fd03c81SAlex Brachet 
188ed7ade9cSJoseph Huber static GetoptContext ctx{&impl::optarg, &impl::optind, &impl::optopt,
189ed7ade9cSJoseph Huber                          &optpos,       &impl::opterr, /*errstream=*/nullptr};
1905fd03c81SAlex Brachet 
191c3228714SGuillaume Chatelet #ifndef LIBC_COPT_PUBLIC_PACKAGING
1925fd03c81SAlex Brachet // This is used exclusively in tests.
1935fd03c81SAlex Brachet void set_getopt_state(char **optarg, int *optind, int *optopt, unsigned *optpos,
194ed7ade9cSJoseph Huber                       int *opterr, FILE *errstream) {
1955fd03c81SAlex Brachet   ctx = {optarg, optind, optopt, optpos, opterr, errstream};
1965fd03c81SAlex Brachet }
1975fd03c81SAlex Brachet #endif
1985fd03c81SAlex Brachet 
1995fd03c81SAlex Brachet } // namespace impl
2005fd03c81SAlex Brachet 
2015fd03c81SAlex Brachet LLVM_LIBC_FUNCTION(int, getopt,
2025fd03c81SAlex Brachet                    (int argc, char *const argv[], const char *optstring)) {
2035fd03c81SAlex Brachet   return getopt_r(argc, argv, optstring, impl::ctx);
2045fd03c81SAlex Brachet }
2055fd03c81SAlex Brachet 
206*5ff3ff33SPetr Hosek } // namespace LIBC_NAMESPACE_DECL
207