mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 08:52:44 +00:00 
			
		
		
		
	 515f31339c
			
		
	
	
		515f31339c
		
	
	
	
	
		
			
			The Linux `getopt_long` manpage tells users to reset `optind` to 1 when scanning the same argument vector or a new argument vector again. This makes sense, since `optind` denotes the _next_ option to be processed. The behavior of setting `optind` to 0 doesn't seem to be specified anywhere, so let's also remove that comment from `unistd.h`.
		
			
				
	
	
		
			94 lines
		
	
	
	
		
			3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			94 lines
		
	
	
	
		
			3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2020, Sergey Bugaev <bugaevc@serenityos.org>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include <AK/OptionParser.h>
 | |
| #include <AK/StringView.h>
 | |
| #include <AK/Vector.h>
 | |
| #include <getopt.h>
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| int opterr = 1;
 | |
| int optopt = 0;
 | |
| int optind = 1;
 | |
| int optreset = 0;
 | |
| char* optarg = nullptr;
 | |
| 
 | |
| // POSIX says, "When an element of argv[] contains multiple option characters,
 | |
| // it is unspecified how getopt() determines which options have already been
 | |
| // processed". Well, this is how we do it.
 | |
| namespace {
 | |
| Vector<StringView> s_args;
 | |
| OptionParser s_parser;
 | |
| }
 | |
| 
 | |
| int getopt(int argc, char* const* argv, char const* short_options)
 | |
| {
 | |
|     s_args.clear_with_capacity();
 | |
|     s_args.ensure_capacity(argc);
 | |
|     for (auto i = 1; i < argc; ++i)
 | |
|         s_args.append({ argv[i], strlen(argv[i]) });
 | |
| 
 | |
|     if (optind == 1 || optreset == 1) {
 | |
|         s_parser.reset_state();
 | |
|         optind = 1;
 | |
|         optreset = 0;
 | |
|     }
 | |
| 
 | |
|     auto result = s_parser.getopt(s_args.span(), { short_options, strlen(short_options) }, {}, {});
 | |
| 
 | |
|     optind += result.consumed_args;
 | |
|     optarg = result.optarg_value.map([](auto x) { return const_cast<char*>(x.characters_without_null_termination()); }).value_or(optarg);
 | |
|     optopt = result.optopt_value.value_or(optopt);
 | |
|     return result.result;
 | |
| }
 | |
| 
 | |
| int getopt_long(int argc, char* const* argv, char const* short_options, const struct option* long_options, int* out_long_option_index)
 | |
| {
 | |
|     s_args.clear_with_capacity();
 | |
|     s_args.ensure_capacity(argc);
 | |
|     for (auto i = 1; i < argc; ++i)
 | |
|         s_args.append({ argv[i], strlen(argv[i]) });
 | |
| 
 | |
|     size_t long_option_count = 0;
 | |
|     for (auto option = long_options; option && option->name; option += 1)
 | |
|         long_option_count++;
 | |
| 
 | |
|     Vector<OptionParser::Option> translated_long_options;
 | |
|     translated_long_options.ensure_capacity(long_option_count);
 | |
|     for (size_t i = 0; i < long_option_count; ++i) {
 | |
|         auto option = &long_options[i];
 | |
| 
 | |
|         translated_long_options.append(OptionParser::Option {
 | |
|             .name = { option->name, strlen(option->name) },
 | |
|             .requirement = option->has_arg == no_argument
 | |
|                 ? AK::OptionParser::ArgumentRequirement::NoArgument
 | |
|                 : option->has_arg == optional_argument
 | |
|                 ? AK::OptionParser::ArgumentRequirement::HasOptionalArgument
 | |
|                 : AK::OptionParser::ArgumentRequirement::HasRequiredArgument,
 | |
|             .flag = option->flag,
 | |
|             .val = option->val,
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     if (optind == 1 || optreset == 1) {
 | |
|         s_parser.reset_state();
 | |
|         optind = 1;
 | |
|         optreset = 0;
 | |
|     }
 | |
| 
 | |
|     auto result = s_parser.getopt(
 | |
|         s_args.span(),
 | |
|         { short_options, strlen(short_options) },
 | |
|         translated_long_options.span(),
 | |
|         out_long_option_index ? *out_long_option_index : Optional<int&>());
 | |
| 
 | |
|     optind += result.consumed_args;
 | |
|     optarg = result.optarg_value.map([](auto x) { return const_cast<char*>(x.characters_without_null_termination()); }).value_or(optarg);
 | |
|     optopt = result.optopt_value.value_or(optopt);
 | |
|     return result.result;
 | |
| }
 |