optparse.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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)
  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, char **argv)
  125. {
  126. options->argv = argv;
  127. options->permute = 1;
  128. options->optind = 1;
  129. options->subopt = 0;
  130. options->optarg = 0;
  131. options->errmsg[0] = '\0';
  132. }
  133. int optparse(struct optparse *options, const char *optstring)
  134. {
  135. int type;
  136. char *next;
  137. char *option = options->argv[options->optind];
  138. options->errmsg[0] = '\0';
  139. options->optopt = 0;
  140. options->optarg = 0;
  141. if (option == 0)
  142. {
  143. return -1;
  144. }
  145. else if (optparse_is_dashdash(option))
  146. {
  147. options->optind++; /* consume "--" */
  148. return -1;
  149. }
  150. else if (!optparse_is_shortopt(option))
  151. {
  152. if (options->permute)
  153. {
  154. int index = options->optind++;
  155. int r = optparse(options, optstring);
  156. optparse_permute(options, index);
  157. options->optind--;
  158. return r;
  159. }
  160. else
  161. {
  162. return -1;
  163. }
  164. }
  165. option += options->subopt + 1;
  166. options->optopt = option[0];
  167. type = optparse_argtype(optstring, option[0]);
  168. next = options->argv[options->optind + 1];
  169. switch (type)
  170. {
  171. case -1:
  172. {
  173. char str[2] = {0, 0};
  174. str[0] = option[0];
  175. options->optind++;
  176. return optparse_error(options, OPTPARSE_MSG_INVALID, str);
  177. }
  178. case OPTPARSE_NONE:
  179. if (option[1])
  180. {
  181. options->subopt++;
  182. }
  183. else
  184. {
  185. options->subopt = 0;
  186. options->optind++;
  187. }
  188. return option[0];
  189. case OPTPARSE_REQUIRED:
  190. options->subopt = 0;
  191. options->optind++;
  192. if (option[1])
  193. {
  194. options->optarg = option + 1;
  195. }
  196. else if (next != 0)
  197. {
  198. options->optarg = next;
  199. options->optind++;
  200. }
  201. else
  202. {
  203. char str[2] = {0, 0};
  204. str[0] = option[0];
  205. options->optarg = 0;
  206. return optparse_error(options, OPTPARSE_MSG_MISSING, str);
  207. }
  208. return option[0];
  209. case OPTPARSE_OPTIONAL:
  210. options->subopt = 0;
  211. options->optind++;
  212. if (option[1])
  213. options->optarg = option + 1;
  214. else
  215. options->optarg = 0;
  216. return option[0];
  217. }
  218. return 0;
  219. }
  220. int optparse_long(struct optparse *options, const struct optparse_long *longopts, int *longindex)
  221. {
  222. int i;
  223. char *option = options->argv[options->optind];
  224. if (option == 0)
  225. {
  226. return -1;
  227. }
  228. else if (optparse_is_dashdash(option))
  229. {
  230. options->optind++; /* consume "--" */
  231. return -1;
  232. }
  233. else if (optparse_is_shortopt(option))
  234. {
  235. return optparse_long_fallback(options, longopts, longindex);
  236. }
  237. else if (!optparse_is_longopt(option))
  238. {
  239. if (options->permute)
  240. {
  241. int index = options->optind++;
  242. int r = optparse_long(options, longopts, longindex);
  243. optparse_permute(options, index);
  244. options->optind--;
  245. return r;
  246. }
  247. else
  248. {
  249. return -1;
  250. }
  251. }
  252. /* Parse as long option. */
  253. options->errmsg[0] = '\0';
  254. options->optopt = 0;
  255. options->optarg = 0;
  256. option += 2; /* skip "--" */
  257. options->optind++;
  258. for (i = 0; !optparse_longopts_end(longopts, i); i++)
  259. {
  260. const char *name = longopts[i].longname;
  261. if (optparse_longopts_match(name, option))
  262. {
  263. char *arg;
  264. if (longindex)
  265. *longindex = i;
  266. options->optopt = longopts[i].shortname;
  267. arg = optparse_longopts_arg(option);
  268. if (longopts[i].argtype == OPTPARSE_NONE && arg != 0)
  269. {
  270. return optparse_error(options, OPTPARSE_MSG_TOOMANY, name);
  271. }
  272. if (arg != 0)
  273. {
  274. options->optarg = arg;
  275. }
  276. else if (longopts[i].argtype == OPTPARSE_REQUIRED)
  277. {
  278. options->optarg = options->argv[options->optind];
  279. if (options->optarg == 0)
  280. return optparse_error(options, OPTPARSE_MSG_MISSING, name);
  281. else
  282. options->optind++;
  283. }
  284. return options->optopt;
  285. }
  286. }
  287. return optparse_error(options, OPTPARSE_MSG_INVALID, option);
  288. }
  289. char *optparse_arg(struct optparse *options)
  290. {
  291. char *option = options->argv[options->optind];
  292. options->subopt = 0;
  293. if (option != 0)
  294. options->optind++;
  295. return option;
  296. }