1 /* Copyright (C) 2014 InfiniDB, Inc.
2
3 This program is free software; you can redistribute it and/or
4 modify it under the terms of the GNU General Public License
5 as published by the Free Software Foundation; version 2 of
6 the License.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
16 MA 02110-1301, USA. */
17
18 // $Id: idb_getopt.cpp 3495 2013-01-21 14:09:51Z rdempsey $
19
20 #include <set>
21 #include <cctype>
22 using namespace std;
23
24 #include "idb_getopt.h"
25
26 struct GetOptData
27 {
28 bool initialized;
29 set<char> opSet;
30 set<char> opWithArgSet;
31 char* nextchar;
GetOptDataGetOptData32 GetOptData() : initialized(false), nextchar(0) {}
33 };
34
35 /* Same as GNU interface. For communication from `getopt' to the caller.
36 When `getopt' finds an option that takes an argument,
37 the argument value is returned here.
38 Also, when `ordering' is RETURN_IN_ORDER,
39 each non-option ARGV-element is returned here. */
40
41 char* optarg;
42
43 /* Same as GNU interface. Index in ARGV of the next element to be scanned.
44 This is used for communication to and from the caller
45 and for communication between successive calls to `getopt'.
46
47 When `getopt' returns -1, this is the index of the first of the
48 non-option elements that the caller should itself scan.
49
50 Otherwise, `optind' communicates from one call to the next
51 how much of ARGV has been scanned so far.*/
52
53 /* 1003.2 says this must be 1 before any call.*/
54 int optind = 1;
55
56 /* Same as GNU interface. Callers store zero here to inhibit the error message
57 for unrecognized options. */
58
59 int opterr = 1;
60
61 /* Same as GNU interface. Set to an option character which was unrecognized.
62 This must be initialized on some systems to avoid linking in the
63 system's own getopt implementation. */
64
65 int optopt = '?';
66
67 namespace
68 {
69 /* Keep a global copy of all internal members of getopt_data. */
70 GetOptData getopt_data;
71
72 // only looks at alphabet and numeric in optstring. The other characters
73 // will be ignored
initialize_opt(const char * optstring)74 void initialize_opt(const char* optstring)
75 {
76 for (unsigned i = 0; i < strlen(optstring); i++)
77 {
78 if (isalnum(optstring[i]))
79 {
80 if ((i < strlen(optstring) - 1) && (optstring[i + 1] == ':'))
81 {
82 getopt_data.opWithArgSet.insert(optstring[i]);
83 i++;
84 }
85 else
86 {
87 getopt_data.opSet.insert(optstring[i]);
88 }
89 }
90 }
91
92 getopt_data.initialized = true;
93 }
94
95 } //namespace
96
getopt(int argc,char * const * argv,const char * optstring)97 int getopt(int argc, char* const* argv, const char* optstring)
98 {
99 if (argc < 1)
100 return -1;
101
102 if (!getopt_data.initialized)
103 initialize_opt(optstring);
104
105 int retchar = 0;
106 char* arg = 0;
107 bool newArg = false;
108 int curind = optind;
109
110 if (optind < argc)
111 {
112 if (getopt_data.nextchar == 0)
113 {
114 arg = argv[optind];
115 newArg = true;
116 }
117 else
118 {
119 arg = getopt_data.nextchar;
120 }
121
122 // all valid op should be started with '-', with or without arg following.
123 // stop process when hitting the first non '-' arg that does not belong to a
124 // previous option.
125 if (newArg && (strlen(arg) <= 1 || *arg != '-'))
126 return -1;
127
128 if (newArg && (strcmp(arg, "--") == 0))
129 {
130 optind++;
131 return -1;
132 }
133
134 if (newArg && *arg == '-')
135 arg++;
136
137 if (arg != 0)
138 {
139 // option without arg
140 if (getopt_data.opSet.find(*arg) != getopt_data.opSet.end())
141 {
142 retchar = *arg;
143
144 if (*(arg + 1) == 0) // "-a"
145 {
146 optind++;
147 getopt_data.nextchar = 0;
148 }
149 else // "-abc"
150 {
151 getopt_data.nextchar = arg + 1;
152 }
153 }
154 // option with arg
155 else if (getopt_data.opWithArgSet.find(*arg) != getopt_data.opWithArgSet.end())
156 {
157 getopt_data.nextchar = 0;
158
159 if (*(arg + 1) == 0) // "-c foo" next arg is arg of c
160 {
161 if (optind < argc - 1)
162 {
163 retchar = *arg;
164 optind++;
165 optarg = argv[optind++];
166 }
167 else // error. no arg provided for argop. this option is invalid
168 {
169 optopt = *arg;
170 optind++;
171 retchar = '?';
172 }
173 }
174 else // "-cfoo" arg is in the string right after option letter
175 {
176 retchar = *arg;
177 optarg = arg + 1;
178 optind++;
179 }
180 }
181 else // invalid option
182 {
183 optopt = *arg;
184 retchar = '?';
185
186 if (*(arg + 1) == 0) // "-x"
187 {
188 optind++;
189 getopt_data.nextchar = 0;
190 }
191 else // "-xbc"
192 {
193 getopt_data.nextchar = arg + 1;
194 }
195 }
196 }
197 }
198
199 if (optind == argc && retchar == 0)
200 {
201 optind = curind;
202 return -1;
203 }
204
205 return retchar;
206 }
207