getopt.cpp 3.0 KB

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