Serenity Operating System
at master 94 lines 3.1 kB view raw
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}