xref: /6.0.3/platform/src/getopt.cc (revision 38010ba3)
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
24char* cb::getopt::optarg;
25int cb::getopt::opterr;
26int cb::getopt::optind = 1;
27int cb::getopt::optopt;
28
29static bool silent = false;
30
31using namespace cb::getopt;
32
33static int parse_longopt(int argc,
34                         char** argv,
35                         const option* longopts,
36                         int* longindex) {
37    std::string name{argv[optind] + 2};
38    if (name.empty()) {
39        ++optind;
40        return -1;
41    }
42
43    auto idx = name.find('=');
44    bool resized = false;
45    if (idx != std::string::npos) {
46        name.resize(idx);
47        resized = true;
48    }
49
50    optarg = nullptr;
51    for (const auto* p = longopts; p != nullptr && p->name; ++p) {
52        if (name == p->name) {
53            // This is it :)
54            if (p->has_arg == required_argument) {
55                // we need a value!
56                if (resized) {
57                    // the value was part of the string:
58                    optarg = argv[optind] + 2 + idx + 1;
59                } else if (++optind == argc) {
60                    if (!silent) {
61                        fprintf(stderr,
62                                "%s: option requires an argument -- %s\n",
63                                argv[0],
64                                name.c_str());
65                    }
66                    return '?';
67                } else {
68                    // The option follows this entry
69                    optarg = argv[optind];
70                }
71            } else if (p->has_arg == optional_argument) {
72                if (resized) {
73                    // the value was part of the string:
74                    // (the +3 is for the leading dashes and the equal sign
75                    optarg = argv[optind] + idx + 3;
76                }
77            }
78            ++optind;
79            return p->val;
80        }
81    }
82
83    // Not found, we should increase optind too,
84    // to continue getopt_long().
85    optind++;
86    return '?';
87}
88
89int cb::getopt::getopt_long(int argc,
90                            char** argv,
91                            const char* optstring,
92                            const option* longopts,
93                            int* longindex) {
94    if (optind + 1 > argc) {
95        // EOF
96        return -1;
97    }
98
99    if (argv[optind][0] != '-') {
100        return -1;
101    }
102
103    if (argv[optind][1] == '-') {
104        // this is a long option
105        return parse_longopt(argc, argv, longopts, longindex);
106    } else if (argv[optind][2] != '\0') {
107        if (!silent) {
108            fprintf(stderr,
109                    "You can't specify multiple options with this "
110                    "implementation\n");
111        }
112        return '?';
113    } else {
114        // this is a short option
115        const char* p = strchr(optstring, argv[optind][1]);
116        int idx = optind;
117        optind++;
118
119        if (p == nullptr) {
120            return '?';
121        }
122
123        if (*(p + 1) == ':') {
124            optarg = argv[optind];
125            optind++;
126            if (optarg == nullptr || optind > argc) {
127                if (!silent) {
128                    fprintf(stderr,
129                            "%s: option requires an argument -- %s\n",
130                            argv[0],
131                            argv[idx] + 1);
132                }
133                return '?';
134            }
135        } else {
136            optarg = nullptr;
137        }
138        return argv[idx][1];
139    }
140}
141
142int cb::getopt::getopt(int argc, char** argv, const char* optstring) {
143    return getopt_long(argc, argv, optstring, nullptr, nullptr);
144}
145
146void cb::getopt::reset() {
147    optarg = nullptr;
148    opterr = 0;
149    optind = 1;
150    optopt = 0;
151}
152
153void cb::getopt::mute_stderr() {
154    silent = true;
155}
156