parse.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /* Actual code to parse commandline. */
  2. #include <ccan/opt/opt.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. #include <assert.h>
  6. #include "private.h"
  7. /* glibc does this as:
  8. /tmp/opt-example: invalid option -- 'x'
  9. /tmp/opt-example: unrecognized option '--long'
  10. /tmp/opt-example: option '--someflag' doesn't allow an argument
  11. /tmp/opt-example: option '--s' is ambiguous
  12. /tmp/opt-example: option requires an argument -- 's'
  13. */
  14. static int parse_err(void (*errlog)(const char *fmt, ...),
  15. const char *argv0, const char *arg, unsigned len,
  16. const char *problem)
  17. {
  18. errlog("%s: %.*s: %s", argv0, len, arg, problem);
  19. return -1;
  20. }
  21. static void consume_option(int *argc, char *argv[], unsigned optnum)
  22. {
  23. memmove(&argv[optnum], &argv[optnum+1],
  24. sizeof(argv[optnum]) * (*argc-optnum));
  25. (*argc)--;
  26. }
  27. /* Returns 1 if argument consumed, 0 if all done, -1 on error. */
  28. int parse_one(int *argc, char *argv[], unsigned *offset,
  29. void (*errlog)(const char *fmt, ...))
  30. {
  31. unsigned i, arg, len;
  32. const char *o, *optarg = NULL;
  33. char *problem;
  34. if (getenv("POSIXLY_CORRECT")) {
  35. /* Don't find options after non-options. */
  36. arg = 1;
  37. } else {
  38. for (arg = 1; argv[arg]; arg++) {
  39. if (argv[arg][0] == '-')
  40. break;
  41. }
  42. }
  43. if (!argv[arg] || argv[arg][0] != '-')
  44. return 0;
  45. /* Special arg terminator option. */
  46. if (strcmp(argv[arg], "--") == 0) {
  47. consume_option(argc, argv, arg);
  48. return 0;
  49. }
  50. /* Long options start with -- */
  51. if (argv[arg][1] == '-') {
  52. assert(*offset == 0);
  53. for (o = first_lopt(&i, &len); o; o = next_lopt(o, &i, &len)) {
  54. if (strncmp(argv[arg] + 2, o, len) != 0)
  55. continue;
  56. if (argv[arg][2 + len] == '=')
  57. optarg = argv[arg] + 2 + len + 1;
  58. else if (argv[arg][2 + len] != '\0')
  59. continue;
  60. break;
  61. }
  62. if (!o)
  63. return parse_err(errlog, argv[0],
  64. argv[arg], strlen(argv[arg]),
  65. "unrecognized option");
  66. /* For error messages, we include the leading '--' */
  67. o -= 2;
  68. len += 2;
  69. } else {
  70. /* offset allows us to handle -abc */
  71. for (o = first_sopt(&i); o; o = next_sopt(o, &i)) {
  72. if (argv[arg][*offset + 1] != *o)
  73. continue;
  74. (*offset)++;
  75. break;
  76. }
  77. if (!o)
  78. return parse_err(errlog, argv[0],
  79. argv[arg], strlen(argv[arg]),
  80. "unrecognized option");
  81. /* For error messages, we include the leading '-' */
  82. o--;
  83. len = 2;
  84. }
  85. if (opt_table[i].type == OPT_NOARG) {
  86. if (optarg)
  87. return parse_err(errlog, argv[0], o, len,
  88. "doesn't allow an argument");
  89. problem = opt_table[i].cb(opt_table[i].u.arg);
  90. } else {
  91. if (!optarg) {
  92. /* Swallow any short options as optarg, eg -afile */
  93. if (*offset && argv[arg][*offset + 1]) {
  94. optarg = argv[arg] + *offset + 1;
  95. *offset = 0;
  96. } else
  97. optarg = argv[arg+1];
  98. }
  99. if (!optarg)
  100. return parse_err(errlog, argv[0], o, len,
  101. "requires an argument");
  102. if (opt_table[i].type == OPT_PROCESSARG)
  103. opt_set_charp(optarg, opt_table[i].u.arg);
  104. problem = opt_table[i].cb_arg(optarg, opt_table[i].u.arg);
  105. }
  106. if (problem) {
  107. parse_err(errlog, argv[0], o, len, problem);
  108. free(problem);
  109. return -1;
  110. }
  111. /* If no more letters in that short opt, reset offset. */
  112. if (*offset && !argv[arg][*offset + 1])
  113. *offset = 0;
  114. /* All finished with that option? */
  115. if (*offset == 0) {
  116. consume_option(argc, argv, arg);
  117. if (optarg && optarg == argv[arg])
  118. consume_option(argc, argv, arg);
  119. }
  120. return 1;
  121. }