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