Serenity Operating System
1/*
2 * Copyright (c) 2020, Sergey Bugaev <bugaevc@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/OptionParser.h>
8#include <AK/StringView.h>
9#include <AK/Vector.h>
10#include <getopt.h>
11#include <stdio.h>
12#include <string.h>
13#include <unistd.h>
14
15int opterr = 1;
16int optopt = 0;
17int optind = 1;
18int optreset = 0;
19char* optarg = nullptr;
20
21// POSIX says, "When an element of argv[] contains multiple option characters,
22// it is unspecified how getopt() determines which options have already been
23// processed". Well, this is how we do it.
24namespace {
25Vector<StringView> s_args;
26OptionParser s_parser;
27}
28
29int getopt(int argc, char* const* argv, char const* short_options)
30{
31 s_args.clear_with_capacity();
32 s_args.ensure_capacity(argc);
33 for (auto i = 1; i < argc; ++i)
34 s_args.append({ argv[i], strlen(argv[i]) });
35
36 if (optind == 1 || optreset == 1) {
37 s_parser.reset_state();
38 optind = 1;
39 optreset = 0;
40 }
41
42 auto result = s_parser.getopt(s_args.span(), { short_options, strlen(short_options) }, {}, {});
43
44 optind += result.consumed_args;
45 optarg = result.optarg_value.map([](auto x) { return const_cast<char*>(x.characters_without_null_termination()); }).value_or(optarg);
46 optopt = result.optopt_value.value_or(optopt);
47 return result.result;
48}
49
50int getopt_long(int argc, char* const* argv, char const* short_options, const struct option* long_options, int* out_long_option_index)
51{
52 s_args.clear_with_capacity();
53 s_args.ensure_capacity(argc);
54 for (auto i = 1; i < argc; ++i)
55 s_args.append({ argv[i], strlen(argv[i]) });
56
57 size_t long_option_count = 0;
58 for (auto option = long_options; option && option->name; option += 1)
59 long_option_count++;
60
61 Vector<OptionParser::Option> translated_long_options;
62 translated_long_options.ensure_capacity(long_option_count);
63 for (size_t i = 0; i < long_option_count; ++i) {
64 auto option = &long_options[i];
65
66 translated_long_options.append(OptionParser::Option {
67 .name = { option->name, strlen(option->name) },
68 .requirement = option->has_arg == no_argument
69 ? AK::OptionParser::ArgumentRequirement::NoArgument
70 : option->has_arg == optional_argument
71 ? AK::OptionParser::ArgumentRequirement::HasOptionalArgument
72 : AK::OptionParser::ArgumentRequirement::HasRequiredArgument,
73 .flag = option->flag,
74 .val = option->val,
75 });
76 }
77
78 if (optind == 1 || optreset == 1) {
79 s_parser.reset_state();
80 optind = 1;
81 optreset = 0;
82 }
83
84 auto result = s_parser.getopt(
85 s_args.span(),
86 { short_options, strlen(short_options) },
87 translated_long_options.span(),
88 out_long_option_index ? *out_long_option_index : Optional<int&>());
89
90 optind += result.consumed_args;
91 optarg = result.optarg_value.map([](auto x) { return const_cast<char*>(x.characters_without_null_termination()); }).value_or(optarg);
92 optopt = result.optopt_value.value_or(optopt);
93 return result.result;
94}