optparse.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /*
  2. * @File: optparse.c
  3. * @Author: liu2guang
  4. * @Date: 2018-06-20 14:52:10
  5. *
  6. * @LICENSE: MIT
  7. * https://github.com/liu2guang/optparse/blob/master/LICENSE
  8. *
  9. * Change Logs:
  10. * Date Author Notes
  11. * 2018-06-20 liu2guang Adapter RT-Thread.
  12. */
  13. #include "optparse.h"
  14. #define OPTPARSE_MSG_INVALID "invalid option"
  15. #define OPTPARSE_MSG_MISSING "option requires an argument"
  16. #define OPTPARSE_MSG_TOOMANY "option takes no arguments"
  17. int optparse_error(struct optparse *options, const char *msg, const char *data)
  18. {
  19. unsigned p = 0;
  20. const char *sep = " -- '";
  21. while (*msg)
  22. options->errmsg[p++] = *msg++;
  23. while (*sep)
  24. options->errmsg[p++] = *sep++;
  25. while (p < sizeof(options->errmsg) - 2 && *data)
  26. options->errmsg[p++] = *data++;
  27. options->errmsg[p++] = '\'';
  28. options->errmsg[p++] = '\0';
  29. return '?';
  30. }
  31. static int optparse_is_dashdash(const char *arg)
  32. {
  33. return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0';
  34. }
  35. static int optparse_is_shortopt(const char *arg)
  36. {
  37. return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0';
  38. }
  39. static int optparse_is_longopt(const char *arg)
  40. {
  41. return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0';
  42. }
  43. static void optparse_permute(struct optparse *options, int index)
  44. {
  45. char *nonoption = options->argv[index];
  46. int i;
  47. for (i = index; i < options->optind - 1; i++)
  48. options->argv[i] = options->argv[i + 1];
  49. options->argv[options->optind - 1] = nonoption;
  50. }
  51. static int optparse_argtype(const char *optstring, char c)
  52. {
  53. int count = OPTPARSE_NONE;
  54. if (c == ':')
  55. return -1;
  56. for (; *optstring && c != *optstring; optstring++);
  57. if (!*optstring)
  58. return -1;
  59. if (optstring[1] == ':')
  60. count += optstring[2] == ':' ? 2 : 1;
  61. return count;
  62. }
  63. static int optparse_longopts_end(const struct optparse_long *longopts, int i)
  64. {
  65. return !longopts[i].longname && !longopts[i].shortname;
  66. }
  67. static void optparse_from_long(const struct optparse_long *longopts, char *optstring)
  68. {
  69. char *p = optstring;
  70. int i;
  71. for (i = 0; !optparse_longopts_end(longopts, i); i++)
  72. {
  73. if (longopts[i].shortname && longopts[i].shortname < 127)
  74. {
  75. int a;
  76. *p++ = longopts[i].shortname;
  77. for (a = 0; a < (int)longopts[i].argtype; a++)
  78. *p++ = ':';
  79. }
  80. }
  81. *p = '\0';
  82. }
  83. /* Unlike strcmp(), handles options containing "=". */
  84. static int optparse_longopts_match(const char *longname, const char *option)
  85. {
  86. const char *a = option, *n = longname;
  87. if (longname == 0)
  88. return 0;
  89. for (; *a && *n && *a != '='; a++, n++)
  90. if (*a != *n)
  91. return 0;
  92. return *n == '\0' && (*a == '\0' || *a == '=');
  93. }
  94. /* Return the part after "=", or NULL. */
  95. static char *optparse_longopts_arg(char *option)
  96. {
  97. for (; *option && *option != '='; option++);
  98. if (*option == '=')
  99. return option + 1;
  100. else
  101. return 0;
  102. }
  103. static int optparse_long_fallback(struct optparse *options,
  104. const struct optparse_long *longopts,
  105. int *longindex)
  106. {
  107. int result;
  108. char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */
  109. optparse_from_long(longopts, optstring);
  110. result = optparse(options, optstring);
  111. if (longindex != 0)
  112. {
  113. *longindex = -1;
  114. if (result != -1)
  115. {
  116. int i;
  117. for (i = 0; !optparse_longopts_end(longopts, i); i++)
  118. if (longopts[i].shortname == options->optopt)
  119. *longindex = i;
  120. }
  121. }
  122. return result;
  123. }
  124. void optparse_init(struct optparse *options, int argc, char **argv)
  125. {
  126. options->argc = argc;
  127. options->argv = argv;
  128. options->permute = 1;
  129. options->optind = argv[0] != 0;
  130. options->subopt = 0;
  131. options->optarg = 0;
  132. options->errmsg[0] = '\0';
  133. }
  134. int optparse(struct optparse *options, const char *optstring)
  135. {
  136. int type;
  137. char *next;
  138. char *option = options->argv[options->optind];
  139. options->errmsg[0] = '\0';
  140. options->optopt = 0;
  141. options->optarg = 0;
  142. if ((options->optind>= options->argc) || option == 0)
  143. {
  144. return -1;
  145. }
  146. else if (optparse_is_dashdash(option))
  147. {
  148. options->optind++; /* consume "--" */
  149. return -1;
  150. }
  151. else if (!optparse_is_shortopt(option))
  152. {
  153. if (options->permute)
  154. {
  155. int index = options->optind++;
  156. int r = optparse(options, optstring);
  157. optparse_permute(options, index);
  158. options->optind--;
  159. return r;
  160. }
  161. else
  162. {
  163. return -1;
  164. }
  165. }
  166. option += options->subopt + 1;
  167. options->optopt = option[0];
  168. type = optparse_argtype(optstring, option[0]);
  169. next = options->argv[options->optind + 1];
  170. switch (type)
  171. {
  172. case -1:
  173. {
  174. char str[2] = {0, 0};
  175. str[0] = option[0];
  176. options->optind++;
  177. return optparse_error(options, OPTPARSE_MSG_INVALID, str);
  178. }
  179. case OPTPARSE_NONE:
  180. if (option[1])
  181. {
  182. options->subopt++;
  183. }
  184. else
  185. {
  186. options->subopt = 0;
  187. options->optind++;
  188. }
  189. return option[0];
  190. case OPTPARSE_REQUIRED:
  191. options->subopt = 0;
  192. options->optind++;
  193. if (option[1])
  194. {
  195. options->optarg = option + 1;
  196. }
  197. else if (next != 0)
  198. {
  199. options->optarg = next;
  200. options->optind++;
  201. }
  202. else
  203. {
  204. char str[2] = {0, 0};
  205. str[0] = option[0];
  206. options->optarg = 0;
  207. return optparse_error(options, OPTPARSE_MSG_MISSING, str);
  208. }
  209. return option[0];
  210. case OPTPARSE_OPTIONAL:
  211. options->subopt = 0;
  212. options->optind++;
  213. if (option[1])
  214. options->optarg = option + 1;
  215. else
  216. options->optarg = 0;
  217. return option[0];
  218. }
  219. return 0;
  220. }
  221. int optparse_long(struct optparse *options, const struct optparse_long *longopts, int *longindex)
  222. {
  223. int i;
  224. char *option = options->argv[options->optind];
  225. if (option == 0)
  226. {
  227. return -1;
  228. }
  229. else if (optparse_is_dashdash(option))
  230. {
  231. options->optind++; /* consume "--" */
  232. return -1;
  233. }
  234. else if (optparse_is_shortopt(option))
  235. {
  236. return optparse_long_fallback(options, longopts, longindex);
  237. }
  238. else if (!optparse_is_longopt(option))
  239. {
  240. if (options->permute)
  241. {
  242. int index = options->optind++;
  243. int r = optparse_long(options, longopts, longindex);
  244. optparse_permute(options, index);
  245. options->optind--;
  246. return r;
  247. }
  248. else
  249. {
  250. return -1;
  251. }
  252. }
  253. /* Parse as long option. */
  254. options->errmsg[0] = '\0';
  255. options->optopt = 0;
  256. options->optarg = 0;
  257. option += 2; /* skip "--" */
  258. options->optind++;
  259. for (i = 0; !optparse_longopts_end(longopts, i); i++)
  260. {
  261. const char *name = longopts[i].longname;
  262. if (optparse_longopts_match(name, option))
  263. {
  264. char *arg;
  265. if (longindex)
  266. *longindex = i;
  267. options->optopt = longopts[i].shortname;
  268. arg = optparse_longopts_arg(option);
  269. if (longopts[i].argtype == OPTPARSE_NONE && arg != 0)
  270. {
  271. return optparse_error(options, OPTPARSE_MSG_TOOMANY, name);
  272. }
  273. if (arg != 0)
  274. {
  275. options->optarg = arg;
  276. }
  277. else if (longopts[i].argtype == OPTPARSE_REQUIRED)
  278. {
  279. options->optarg = options->argv[options->optind];
  280. if (options->optarg == 0)
  281. return optparse_error(options, OPTPARSE_MSG_MISSING, name);
  282. else
  283. options->optind++;
  284. }
  285. return options->optopt;
  286. }
  287. }
  288. return optparse_error(options, OPTPARSE_MSG_INVALID, option);
  289. }
  290. char *optparse_arg(struct optparse *options)
  291. {
  292. char *option = options->argv[options->optind];
  293. options->subopt = 0;
  294. if (option != 0)
  295. options->optind++;
  296. return option;
  297. }