1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2013 Couchbase, Inc.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 #define BUILDING_LIBPLATFORM 1
18 #include <platform/getopt.h>
19 #include <cctype>
20 #include <cstdio>
21 #include <cstring>
22 #include <string>
23 
24 char* cb::getopt::optarg;
25 int cb::getopt::opterr;
26 int cb::getopt::optind = 1;
27 int cb::getopt::optopt;
28 
29 static bool silent = false;
30 
parse_longopt(int argc, char** argv, const cb::getopt::option* longopts, int* longindex)31 static int parse_longopt(int argc,
32                          char** argv,
33                          const cb::getopt::option* longopts,
34                          int* longindex) {
35     std::string name{argv[cb::getopt::optind] + 2};
36     if (name.empty()) {
37         ++cb::getopt::optind;
38         return -1;
39     }
40 
41     auto idx = name.find('=');
42     bool resized = false;
43     if (idx != std::string::npos) {
44         name.resize(idx);
45         resized = true;
46     }
47 
48     cb::getopt::optarg = nullptr;
49     for (const auto* p = longopts; p != nullptr && p->name; ++p) {
50         if (name == p->name) {
51             // This is it :)
52             if (p->has_arg == cb::getopt::required_argument) {
53                 // we need a value!
54                 if (resized) {
55                     // the value was part of the string:
56                     cb::getopt::optarg = argv[cb::getopt::optind] + 2 + idx + 1;
57                 } else if (++cb::getopt::optind == argc) {
58                     if (!silent) {
59                         fprintf(stderr,
60                                 "%s: option requires an argument -- %s\n",
61                                 argv[0],
62                                 name.c_str());
63                     }
64                     return '?';
65                 } else {
66                     // The option follows this entry
67                     cb::getopt::optarg = argv[cb::getopt::optind];
68                 }
69             } else if (p->has_arg == cb::getopt::optional_argument) {
70                 if (resized) {
71                     // the value was part of the string:
72                     // (the +3 is for the leading dashes and the equal sign
73                     cb::getopt::optarg = argv[cb::getopt::optind] + idx + 3;
74                 }
75             }
76             ++cb::getopt::optind;
77             return p->val;
78         }
79     }
80 
81     // Not found, we should increase optind too,
82     // to continue getopt_long().
83     cb::getopt::optind++;
84     return '?';
85 }
86 
getopt_long(int argc, char** argv, const char* optstring, const cb::getopt::option* longopts, int* longindex)87 int cb::getopt::getopt_long(int argc,
88                             char** argv,
89                             const char* optstring,
90                             const cb::getopt::option* longopts,
91                             int* longindex) {
92     if (cb::getopt::optind + 1 > argc) {
93         // EOF
94         return -1;
95     }
96 
97     if (argv[cb::getopt::optind][0] != '-') {
98         return -1;
99     }
100 
101     if (argv[cb::getopt::optind][1] == '-') {
102         // this is a long option
103         return parse_longopt(argc, argv, longopts, longindex);
104     } else if (argv[cb::getopt::optind][2] != '\0') {
105         if (!silent) {
106             fprintf(stderr,
107                     "You can't specify multiple options with this "
108                     "implementation\n");
109         }
110         return '?';
111     } else {
112         // this is a short option
113         const char* p = strchr(optstring, argv[cb::getopt::optind][1]);
114         int idx = cb::getopt::optind;
115         cb::getopt::optind++;
116 
117         if (p == nullptr) {
118             return '?';
119         }
120 
121         if (*(p + 1) == ':') {
122             cb::getopt::optarg = argv[cb::getopt::optind];
123             cb::getopt::optind++;
124             if (cb::getopt::optarg == nullptr || cb::getopt::optind > argc) {
125                 if (!silent) {
126                     fprintf(stderr,
127                             "%s: option requires an argument -- %s\n",
128                             argv[0],
129                             argv[idx] + 1);
130                 }
131                 return '?';
132             }
133         } else {
134             cb::getopt::optarg = nullptr;
135         }
136         return argv[idx][1];
137     }
138 }
139 
getopt(int argc, char** argv, const char* optstring)140 int cb::getopt::getopt(int argc, char** argv, const char* optstring) {
141     return cb::getopt::getopt_long(argc, argv, optstring, nullptr, nullptr);
142 }
143 
reset()144 void cb::getopt::reset() {
145     cb::getopt::optarg = nullptr;
146     cb::getopt::opterr = 0;
147     cb::getopt::optind = 1;
148     cb::getopt::optopt = 0;
149 }
150 
mute_stderr()151 void cb::getopt::mute_stderr() {
152     silent = true;
153 }
154