getopt.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. * This is a version of the public domain getopt implementation by
  3. * Henry Spencer originally posted to net.sources.
  4. *
  5. * This file is in the public domain.
  6. */
  7. #include <stdio.h>
  8. #include <string.h>
  9. #include "mupdf/fitz/getopt.h"
  10. #include "mupdf/fitz/string-util.h"
  11. char *fz_optarg; /* Global argument pointer. */
  12. int fz_optind = 0; /* Global argv index. */
  13. const fz_getopt_long_options *fz_optlong = NULL;
  14. int fz_optitem = 0; /* Which item in a long opt list is selected? */
  15. static int
  16. match_long_option(int argc, char * const *argv, const fz_getopt_long_options *longopts, char *opt)
  17. {
  18. if (longopts == NULL)
  19. {
  20. fprintf(stderr, "%s: unknown option --%s\n", argv[0], opt);
  21. return '?';
  22. }
  23. while (longopts->option)
  24. {
  25. const char *s = longopts->option;
  26. const char *eq = strchr(s, '=');
  27. size_t z = eq ? (size_t)(eq-s) : strlen(s);
  28. int arg = 0; /* No arg */
  29. if (s[z] == '=')
  30. arg = 1; /* arg = 1 => Arg list given */
  31. else if (z && s[z-1] == ':')
  32. arg = 2, z--; /* arg = 2 -> argument expected */
  33. /* If we don't match, try the next one. */
  34. /* If we aren't expecting an argument, and the given string is longer than expected, try the next one. */
  35. /* If we are expecting an argument, and the given string doesn't either terminate or end in '=', try the next one. */
  36. if (strncmp(s, opt, z) || (arg == 0 && opt[z] != 0) || (arg != 0 && opt[z] != 0 && opt[z] != '='))
  37. {
  38. longopts++;
  39. continue;
  40. }
  41. /* So we have a match. */
  42. fz_optind++;
  43. if (opt[z])
  44. fz_optarg = &opt[z+1];
  45. else if (fz_optind < argc)
  46. fz_optarg = argv[fz_optind++];
  47. else
  48. {
  49. fprintf(stderr, "%s: option requires argument --%s\n", argv[0], s);
  50. return ':';
  51. }
  52. /* If there is an arg from a predefined list, try to match that here. */
  53. if (arg == 1)
  54. {
  55. fz_optitem = fz_opt_from_list(&opt[z+1], &s[z+1]);
  56. if (longopts->flag)
  57. *longopts->flag = fz_optitem;
  58. if (fz_optitem < 0)
  59. return '?';
  60. }
  61. else if (arg == 2 && longopts->flag)
  62. {
  63. char *p = strchr(fz_optarg, ',');
  64. if (*fz_optarg == 0)
  65. {
  66. fprintf(stderr, "%s: option requires argument --%s\n", argv[0], s);
  67. return ':';
  68. }
  69. else if (!fz_strcasecmp(fz_optarg, "yes") || !fz_strcasecmp(fz_optarg, "on") || !fz_strcasecmp(fz_optarg, "true"))
  70. *longopts->flag = 1;
  71. else if (!fz_strcasecmp(fz_optarg, "no") || !fz_strcasecmp(fz_optarg, "off") || !fz_strcasecmp(fz_optarg, "false"))
  72. *longopts->flag = 0;
  73. else
  74. *longopts->flag = fz_atoi(fz_optarg);
  75. fz_optarg = p ? p+1 : NULL;
  76. }
  77. fz_optlong = longopts;
  78. return 0;
  79. }
  80. fprintf(stderr, "%s: unknown option --%s\n", argv[0], opt);
  81. return '?';
  82. }
  83. static char *scan = NULL; /* Private scan pointer. */
  84. int
  85. fz_getopt_long(int argc, char * const *argv, const char *optstring, const fz_getopt_long_options *longopts)
  86. {
  87. int c;
  88. const char *place;
  89. fz_optarg = NULL;
  90. fz_optlong = NULL;
  91. fz_optitem = -1;
  92. while (!scan || *scan == '\0')
  93. {
  94. if (fz_optind == 0)
  95. fz_optind++;
  96. if (fz_optind >= argc || argv[fz_optind][0] != '-' || argv[fz_optind][1] == '\0')
  97. return EOF;
  98. if (argv[fz_optind][1] == '-' && argv[fz_optind][2] == '\0') {
  99. fz_optind++;
  100. return EOF;
  101. }
  102. if (argv[fz_optind][1] == '-')
  103. {
  104. scan = NULL;
  105. return match_long_option(argc, argv, longopts, &argv[fz_optind][2]);
  106. }
  107. scan = argv[fz_optind]+1;
  108. fz_optind++;
  109. }
  110. c = *scan++;
  111. place = strchr(optstring, c);
  112. if (!place || c == ':') {
  113. fprintf(stderr, "%s: unknown option -%c\n", argv[0], c);
  114. return '?';
  115. }
  116. place++;
  117. if (*place == ':') {
  118. if (*scan != '\0') {
  119. fz_optarg = scan;
  120. scan = NULL;
  121. } else if( fz_optind < argc ) {
  122. fz_optarg = argv[fz_optind];
  123. fz_optind++;
  124. } else {
  125. fprintf(stderr, "%s: option requires argument -%c\n", argv[0], c);
  126. return ':';
  127. }
  128. }
  129. return c;
  130. }
  131. int
  132. fz_getopt(int argc, char *const *argv, const char *optstring)
  133. {
  134. return fz_getopt_long(argc, argv, optstring, NULL);
  135. }
  136. int
  137. fz_opt_from_list(char *opt, const char *optlist)
  138. {
  139. int n = 0;
  140. while (*optlist)
  141. {
  142. const char *optend = optlist;
  143. if (*optend == '*')
  144. {
  145. fz_optarg = opt;
  146. return n;
  147. }
  148. while (*optend != 0 && *optend != '|' && *optend != ':')
  149. optend++;
  150. if (fz_strncasecmp(optlist, opt, optend-optlist))
  151. {
  152. /* We didn't match. Carry on. */
  153. }
  154. else if (opt[optend-optlist] == 0)
  155. {
  156. /* We matched, ending in NUL */
  157. fz_optarg = NULL;
  158. return n;
  159. }
  160. else if (*optend == ':' && opt[optend-optlist] == ':')
  161. {
  162. /* We matched, and we both have some arguments and expected some. */
  163. fz_optarg = &opt[optend-optlist+1];
  164. return n;
  165. }
  166. n++;
  167. if (*optend == ':')
  168. {
  169. optend++;
  170. if (*optend == '|')
  171. optend++;
  172. else if (*optend != 0)
  173. {
  174. fprintf(stderr, "Malformed options string");
  175. return -1;
  176. }
  177. }
  178. if (*optend == '|')
  179. optend++;
  180. optlist = optend;
  181. }
  182. fprintf(stderr, "Unrecognised option argument: %s\n", opt);
  183. return -1;
  184. }