]>
Commit | Line | Data |
---|---|---|
28bca1f7 | 1 | /* |
b8ae064d | 2 | * Copyright (C) 1996-2023 The Squid Software Foundation and contributors |
28bca1f7 EB |
3 | * |
4 | * Squid software is distributed under GPLv2+ license and includes | |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
7 | */ | |
8 | ||
9 | #include "squid.h" | |
10 | ||
11 | #include "CommandLine.h" | |
12 | #include "sbuf/SBuf.h" | |
13 | ||
14 | static void | |
15 | ResetGetopt(const bool allowStderrWarnings) | |
16 | { | |
17 | opterr = allowStderrWarnings; | |
18 | // Resetting optind to zero instead of conventional '1' has an | |
19 | // advantage, since it also resets getopt(3) global state. | |
20 | // getopt(3) always skips argv[0], even if optind is zero | |
21 | optind = 0; | |
22 | } | |
23 | ||
24 | CommandLine::CommandLine(int argC, char *argV[], const char *shortRules, const RawLongOption *longRules): | |
25 | argv_(), | |
26 | shortOptions_(shortRules ? xstrdup(shortRules) : ""), | |
27 | longOptions_() | |
28 | { | |
29 | assert(argC > 0); // C++ main() requirement that makes our arg0() safe | |
30 | assert(shortRules); | |
31 | ||
32 | /* copy argV items */ | |
33 | argv_.reserve(argC+1); | |
34 | for (int i = 0; i < argC; ++i) | |
35 | argv_.push_back(xstrdup(argV[i])); | |
36 | argv_.push_back(nullptr); // POSIX argv "must be terminated by a null pointer" | |
37 | ||
38 | /* copy grammar rules for the long options */ | |
39 | if (longRules) { | |
40 | for (auto longOption = longRules; longOption->name; ++longOption) | |
41 | longOptions_.emplace_back(*longOption); | |
42 | longOptions_.emplace_back(); | |
43 | } | |
44 | } | |
45 | ||
46 | CommandLine::CommandLine(const CommandLine &them): | |
47 | CommandLine(them.argc(), them.argv(), them.shortOptions_, them.longOptions()) | |
48 | { | |
49 | } | |
50 | ||
51 | CommandLine & | |
52 | CommandLine::operator =(const CommandLine &them) | |
53 | { | |
54 | // cannot just swap(*this, them): std::swap(T,T) may call this assignment op | |
55 | CommandLine tmp(them); | |
56 | std::swap(argv_, tmp.argv_); | |
57 | std::swap(shortOptions_, tmp.shortOptions_); | |
58 | std::swap(longOptions_, tmp.longOptions_); | |
59 | return *this; | |
60 | } | |
61 | ||
62 | CommandLine::~CommandLine() | |
63 | { | |
64 | for (auto arg: argv_) | |
65 | xfree(arg); | |
66 | ||
67 | xfree(shortOptions_); | |
68 | } | |
69 | ||
70 | bool | |
71 | CommandLine::hasOption(const int optIdToFind, const char **optValue) const | |
72 | { | |
73 | ResetGetopt(false); // avoid duped warnings; forEachOption() will complain | |
74 | int optId = 0; | |
75 | while (nextOption(optId)) { | |
76 | if (optId == optIdToFind) { | |
77 | if (optValue) { | |
78 | // do not need to copy the optarg string because it is a pointer into the original | |
79 | // argv array (https://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html) | |
80 | *optValue = optarg; | |
81 | } | |
82 | return true; | |
83 | } | |
84 | } | |
85 | return false; | |
86 | } | |
87 | ||
88 | void | |
89 | CommandLine::forEachOption(Visitor visitor) const | |
90 | { | |
91 | ResetGetopt(true); | |
92 | int optId = 0; | |
93 | while (nextOption(optId)) | |
94 | visitor(optId, optarg); | |
95 | } | |
96 | ||
97 | /// extracts the next option (if any) | |
98 | /// \returns whether the option was extracted | |
99 | /// throws on unknown option or missing required argument | |
100 | bool | |
101 | CommandLine::nextOption(int &optId) const | |
102 | { | |
103 | optId = getopt_long(argc(), argv(), shortOptions_, longOptions(), nullptr); | |
104 | if ((optId == ':' && shortOptions_[0] == ':') || optId == '?') { | |
105 | assert(optind > 0 && static_cast<unsigned int>(optind) < argv_.size()); | |
106 | SBuf errMsg; | |
107 | errMsg.Printf("'%s': %s", argv_[optind - 1], optId == '?' ? | |
108 | "unrecognized option or missing required argument" : "missing required argument"); | |
109 | throw TexcHere(errMsg); | |
110 | } | |
111 | return optId != -1; | |
112 | } | |
113 | ||
114 | void | |
115 | CommandLine::resetArg0(const char *programName) | |
116 | { | |
117 | assert(programName); | |
118 | xfree(argv_[0]); | |
119 | argv_[0] = xstrdup(programName); | |
120 | } | |
121 | ||
122 | void | |
123 | CommandLine::pushFrontOption(const char *name, const char *value) | |
124 | { | |
125 | assert(name); | |
126 | argv_.insert(argv_.begin() + 1, xstrdup(name)); | |
127 | if (value) | |
128 | argv_.insert(argv_.begin() + 2, xstrdup(value)); | |
129 | } | |
130 | ||
131 | LongOption::LongOption() : | |
132 | option({nullptr, 0, nullptr, 0}) | |
133 | { | |
134 | } | |
135 | ||
136 | LongOption::LongOption(const RawLongOption &opt) : | |
137 | option({nullptr, 0, nullptr, 0}) | |
138 | { | |
139 | copy(opt); | |
140 | } | |
141 | ||
142 | LongOption::LongOption(const LongOption &opt): | |
143 | LongOption(static_cast<const RawLongOption &>(opt)) | |
144 | { | |
145 | } | |
146 | ||
147 | LongOption::~LongOption() | |
148 | { | |
149 | xfree(name); | |
150 | } | |
151 | ||
152 | LongOption & | |
153 | LongOption::operator =(const LongOption &opt) | |
154 | { | |
155 | if (this != &opt) | |
156 | copy(static_cast<const RawLongOption &>(opt)); | |
157 | return *this; | |
158 | } | |
159 | ||
160 | void | |
161 | LongOption::copy(const RawLongOption &opt) | |
162 | { | |
163 | xfree(name); | |
164 | name = opt.name ? xstrdup(opt.name) : nullptr; | |
165 | has_arg = opt.has_arg; | |
166 | flag = opt.flag; | |
167 | val = opt.val; | |
168 | } | |
169 |