xref: /6.0.3/platform/src/getopt.cc (revision aabde797)
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
31static 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
87int 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
140int cb::getopt::getopt(int argc, char** argv, const char* optstring) {
141    return cb::getopt::getopt_long(argc, argv, optstring, nullptr, nullptr);
142}
143
144void 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
151void cb::getopt::mute_stderr() {
152    silent = true;
153}
154