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 = [¤t, &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