You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

199 lines
4.7 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright © 2002—2018 Sam Hocevar <sam@hocevar.net>
  5. //
  6. // Lol Engine is free software. It comes without any warranty, to
  7. // the extent permitted by applicable law. You can redistribute it
  8. // and/or modify it under the terms of the Do What the Fuck You Want
  9. // to Public License, Version 2, as published by the WTFPL Task Force.
  10. // See http://www.wtfpl.net/ for more details.
  11. //
  12. #include <lol/engine-internal.h>
  13. #if HAVE_GETOPT_H
  14. # include <getopt.h>
  15. #endif
  16. #if HAVE_UNISTD_H
  17. # include <unistd.h>
  18. #endif
  19. namespace lol
  20. {
  21. struct getopt_private
  22. {
  23. getopt_private(int argc, char * const * _argv)
  24. : m_argc(argc),
  25. m_argv(_argv)
  26. {}
  27. #if HAVE_GETOPT_LONG
  28. typedef option optdesc;
  29. #else
  30. struct optdesc
  31. {
  32. char const *name;
  33. int has_arg;
  34. int *flag;
  35. int val;
  36. };
  37. #endif
  38. int m_argc;
  39. char * const *m_argv;
  40. std::string m_optstring;
  41. array<optdesc> m_opts;
  42. };
  43. getopt::getopt(int argc, char ** _argv)
  44. : index(1),
  45. arg(nullptr),
  46. m_private(new getopt_private(argc, _argv))
  47. {
  48. }
  49. getopt::getopt(int argc, char * const * _argv)
  50. : index(1),
  51. arg(nullptr),
  52. m_private(new getopt_private(argc, _argv))
  53. {
  54. }
  55. getopt::~getopt()
  56. {
  57. }
  58. void getopt::add_opt(int short_opt, char const *long_opt, bool has_arg)
  59. {
  60. getopt_private::optdesc o { long_opt, has_arg, nullptr, short_opt };
  61. m_private->m_opts.push(o);
  62. /* “The standards require that the argument [to isalnum()] is either
  63. * EOF or a value that is representable in the type unsigned char.” */
  64. if ((int)(unsigned char)short_opt == short_opt && isalnum(short_opt))
  65. {
  66. m_private->m_optstring += (char)short_opt;
  67. if (has_arg)
  68. m_private->m_optstring += ':';
  69. }
  70. }
  71. int getopt::parse()
  72. {
  73. int longindex = 0; // FIXME: what is this?
  74. #if HAVE_GETOPT_LONG
  75. int ret;
  76. optind = this->index;
  77. optarg = this->arg;
  78. m_private->m_opts.push(getopt_private::optdesc { nullptr, 0, nullptr, 0 });
  79. ret = getopt_long(m_private->m_argc, m_private->m_argv, m_private->m_optstring.c_str(),
  80. (option const *)m_private->m_opts.data(), &longindex);
  81. this->index = optind;
  82. this->arg = optarg;
  83. return ret;
  84. #else
  85. /* XXX: this getopt_long implementation should not be trusted for other
  86. * applications without any serious peer reviewing. It “just works” with
  87. * zzuf and a few libcaca programs but may fail miserably in other
  88. * programs. */
  89. char **argv = (char **)(uintptr_t)m_private->m_argv;
  90. char *flag;
  91. if (this->index >= m_private->m_argc)
  92. return -1;
  93. flag = argv[this->index];
  94. if (flag[0] == '-' && flag[1] != '-')
  95. {
  96. char const *tmp;
  97. int ret = flag[1];
  98. if (ret == '\0')
  99. return -1;
  100. tmp = strchr(m_private->m_optstring.c_str(), ret);
  101. if (!tmp || ret == ':')
  102. return '?';
  103. ++this->index;
  104. if (tmp[1] == ':')
  105. {
  106. if (flag[2] != '\0')
  107. this->arg = flag + 2;
  108. else
  109. {
  110. if (this->index >= m_private->m_argc)
  111. goto too_few;
  112. this->arg = argv[this->index++];
  113. }
  114. return ret;
  115. }
  116. if (flag[2] != '\0')
  117. {
  118. flag[1] = '-';
  119. --this->index;
  120. ++argv[this->index];
  121. }
  122. return ret;
  123. }
  124. if (flag[0] == '-' && flag[1] == '-')
  125. {
  126. if (flag[2] == '\0')
  127. return -1;
  128. for (int i = 0; m_private->m_opts[i].name; ++i)
  129. {
  130. size_t l = strlen(m_private->m_opts[i].name);
  131. if (strncmp(flag + 2, m_private->m_opts[i].name, l))
  132. continue;
  133. switch (flag[2 + l])
  134. {
  135. case '=':
  136. if (!m_private->m_opts[i].has_arg)
  137. goto bad_opt;
  138. longindex = i;
  139. ++this->index;
  140. this->arg = flag + 2 + l + 1;
  141. return m_private->m_opts[i].val;
  142. case '\0':
  143. longindex = i;
  144. ++this->index;
  145. if (m_private->m_opts[i].has_arg)
  146. {
  147. if (this->index >= m_private->m_argc)
  148. goto too_few;
  149. this->arg = argv[this->index++];
  150. }
  151. return m_private->m_opts[i].val;
  152. default:
  153. break;
  154. }
  155. }
  156. bad_opt:
  157. fprintf(stderr, "%s: unrecognized option `%s'\n", argv[0], flag);
  158. return '?';
  159. too_few:
  160. fprintf(stderr, "%s: option `%s' requires an argument\n",
  161. argv[0], flag);
  162. return '?';
  163. }
  164. return -1;
  165. #endif
  166. }
  167. } // namespace lol