getopt.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. /*
  2. * getopt.c - Enhanced implementation of BSD getopt(1)
  3. * Copyright (c) 1997, 1998, 1999, 2000 Frodo Looijaard <frodol@dds.nl>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18. */
  19. /*
  20. * Version 1.0-b4: Tue Sep 23 1997. First public release.
  21. * Version 1.0: Wed Nov 19 1997.
  22. * Bumped up the version number to 1.0
  23. * Fixed minor typo (CSH instead of TCSH)
  24. * Version 1.0.1: Tue Jun 3 1998
  25. * Fixed sizeof instead of strlen bug
  26. * Bumped up the version number to 1.0.1
  27. * Version 1.0.2: Thu Jun 11 1998 (not present)
  28. * Fixed gcc-2.8.1 warnings
  29. * Fixed --version/-V option (not present)
  30. * Version 1.0.5: Tue Jun 22 1999
  31. * Make -u option work (not present)
  32. * Version 1.0.6: Tue Jun 27 2000
  33. * No important changes
  34. * Version 1.1.0: Tue Jun 30 2000
  35. * Added NLS support (partly written by Arkadiusz Mi<B6>kiewicz
  36. * <misiek@misiek.eu.org>)
  37. * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org>
  38. * Removed --version/-V and --help/-h in
  39. * Removed parse_error(), using bb_error_msg() from Busybox instead
  40. * Replaced our_malloc with xmalloc and our_realloc with xrealloc
  41. *
  42. */
  43. #include <stdio.h>
  44. #include <stdlib.h>
  45. #include <string.h>
  46. #include <unistd.h>
  47. #include <ctype.h>
  48. #include <getopt.h>
  49. #include "busybox.h"
  50. /* NON_OPT is the code that is returned when a non-option is found in '+'
  51. mode */
  52. static const int NON_OPT = 1;
  53. /* LONG_OPT is the code that is returned when a long option is found. */
  54. static const int LONG_OPT = 2;
  55. /* The shells recognized. */
  56. typedef enum {BASH,TCSH} shell_t;
  57. /* Some global variables that tells us how to parse. */
  58. static shell_t shell=BASH; /* The shell we generate output for. */
  59. static int quiet_errors=0; /* 0 is not quiet. */
  60. static int quiet_output=0; /* 0 is not quiet. */
  61. static int quote=1; /* 1 is do quote. */
  62. static int alternative=0; /* 0 is getopt_long, 1 is getopt_long_only */
  63. /* Function prototypes */
  64. static const char *normalize(const char *arg);
  65. static int generate_output(char * argv[],int argc,const char *optstr,
  66. const struct option *longopts);
  67. static void add_long_options(char *options);
  68. static void add_longopt(const char *name,int has_arg);
  69. static void set_shell(const char *new_shell);
  70. /*
  71. * This function 'normalizes' a single argument: it puts single quotes around
  72. * it and escapes other special characters. If quote is false, it just
  73. * returns its argument.
  74. * Bash only needs special treatment for single quotes; tcsh also recognizes
  75. * exclamation marks within single quotes, and nukes whitespace.
  76. * This function returns a pointer to a buffer that is overwritten by
  77. * each call.
  78. */
  79. const char *normalize(const char *arg)
  80. {
  81. static char *BUFFER=NULL;
  82. const char *argptr=arg;
  83. char *bufptr;
  84. free(BUFFER);
  85. if (!quote) { /* Just copy arg */
  86. BUFFER=bb_xstrdup(arg);
  87. return BUFFER;
  88. }
  89. /* Each character in arg may take up to four characters in the result:
  90. For a quote we need a closing quote, a backslash, a quote and an
  91. opening quote! We need also the global opening and closing quote,
  92. and one extra character for '\0'. */
  93. BUFFER=xmalloc(strlen(arg)*4+3);
  94. bufptr=BUFFER;
  95. *bufptr++='\'';
  96. while (*argptr) {
  97. if (*argptr == '\'') {
  98. /* Quote: replace it with: '\'' */
  99. *bufptr++='\'';
  100. *bufptr++='\\';
  101. *bufptr++='\'';
  102. *bufptr++='\'';
  103. } else if (shell==TCSH && *argptr=='!') {
  104. /* Exclamation mark: replace it with: \! */
  105. *bufptr++='\'';
  106. *bufptr++='\\';
  107. *bufptr++='!';
  108. *bufptr++='\'';
  109. } else if (shell==TCSH && *argptr=='\n') {
  110. /* Newline: replace it with: \n */
  111. *bufptr++='\\';
  112. *bufptr++='n';
  113. } else if (shell==TCSH && isspace(*argptr)) {
  114. /* Non-newline whitespace: replace it with \<ws> */
  115. *bufptr++='\'';
  116. *bufptr++='\\';
  117. *bufptr++=*argptr;
  118. *bufptr++='\'';
  119. } else
  120. /* Just copy */
  121. *bufptr++=*argptr;
  122. argptr++;
  123. }
  124. *bufptr++='\'';
  125. *bufptr++='\0';
  126. return BUFFER;
  127. }
  128. /*
  129. * Generate the output. argv[0] is the program name (used for reporting errors).
  130. * argv[1..] contains the options to be parsed. argc must be the number of
  131. * elements in argv (ie. 1 if there are no options, only the program name),
  132. * optstr must contain the short options, and longopts the long options.
  133. * Other settings are found in global variables.
  134. */
  135. int generate_output(char * argv[],int argc,const char *optstr,
  136. const struct option *longopts)
  137. {
  138. int exit_code = 0; /* We assume everything will be OK */
  139. int opt;
  140. int longindex;
  141. const char *charptr;
  142. if (quiet_errors) /* No error reporting from getopt(3) */
  143. opterr=0;
  144. optind=0; /* Reset getopt(3) */
  145. while ((opt = (alternative?
  146. getopt_long_only(argc,argv,optstr,longopts,&longindex):
  147. getopt_long(argc,argv,optstr,longopts,&longindex)))
  148. != EOF)
  149. if (opt == '?' || opt == ':' )
  150. exit_code = 1;
  151. else if (!quiet_output) {
  152. if (opt == LONG_OPT) {
  153. printf(" --%s",longopts[longindex].name);
  154. if (longopts[longindex].has_arg)
  155. printf(" %s",
  156. normalize(optarg?optarg:""));
  157. } else if (opt == NON_OPT)
  158. printf(" %s",normalize(optarg));
  159. else {
  160. printf(" -%c",opt);
  161. charptr = strchr(optstr,opt);
  162. if (charptr != NULL && *++charptr == ':')
  163. printf(" %s",
  164. normalize(optarg?optarg:""));
  165. }
  166. }
  167. if (! quiet_output) {
  168. printf(" --");
  169. while (optind < argc)
  170. printf(" %s",normalize(argv[optind++]));
  171. printf("\n");
  172. }
  173. return exit_code;
  174. }
  175. static struct option *long_options=NULL;
  176. static int long_options_length=0; /* Length of array */
  177. static int long_options_nr=0; /* Nr of used elements in array */
  178. static const int LONG_OPTIONS_INCR = 10;
  179. #define init_longopt() add_longopt(NULL,0)
  180. /* Register a long option. The contents of name is copied. */
  181. void add_longopt(const char *name,int has_arg)
  182. {
  183. if (!name) { /* init */
  184. free(long_options);
  185. long_options=NULL;
  186. long_options_length=0;
  187. long_options_nr=0;
  188. }
  189. if (long_options_nr == long_options_length) {
  190. long_options_length += LONG_OPTIONS_INCR;
  191. long_options=xrealloc(long_options,
  192. sizeof(struct option) *
  193. long_options_length);
  194. }
  195. long_options[long_options_nr].name=NULL;
  196. long_options[long_options_nr].has_arg=0;
  197. long_options[long_options_nr].flag=NULL;
  198. long_options[long_options_nr].val=0;
  199. if (long_options_nr) { /* Not for init! */
  200. long_options[long_options_nr-1].has_arg=has_arg;
  201. long_options[long_options_nr-1].flag=NULL;
  202. long_options[long_options_nr-1].val=LONG_OPT;
  203. long_options[long_options_nr-1].name=bb_xstrdup(name);
  204. }
  205. long_options_nr++;
  206. }
  207. /*
  208. * Register several long options. options is a string of long options,
  209. * separated by commas or whitespace.
  210. * This nukes options!
  211. */
  212. void add_long_options(char *options)
  213. {
  214. int arg_opt, tlen;
  215. char *tokptr=strtok(options,", \t\n");
  216. while (tokptr) {
  217. arg_opt=no_argument;
  218. tlen=strlen(tokptr);
  219. if (tlen > 0) {
  220. if (tokptr[tlen-1] == ':') {
  221. if (tlen > 1 && tokptr[tlen-2] == ':') {
  222. tokptr[tlen-2]='\0';
  223. tlen -= 2;
  224. arg_opt=optional_argument;
  225. } else {
  226. tokptr[tlen-1]='\0';
  227. tlen -= 1;
  228. arg_opt=required_argument;
  229. }
  230. if (tlen == 0)
  231. bb_error_msg("empty long option after -l or --long argument");
  232. }
  233. add_longopt(tokptr,arg_opt);
  234. }
  235. tokptr=strtok(NULL,", \t\n");
  236. }
  237. }
  238. void set_shell(const char *new_shell)
  239. {
  240. if (!strcmp(new_shell,"bash"))
  241. shell=BASH;
  242. else if (!strcmp(new_shell,"tcsh"))
  243. shell=TCSH;
  244. else if (!strcmp(new_shell,"sh"))
  245. shell=BASH;
  246. else if (!strcmp(new_shell,"csh"))
  247. shell=TCSH;
  248. else
  249. bb_error_msg("unknown shell after -s or --shell argument");
  250. }
  251. /* Exit codes:
  252. * 0) No errors, successful operation.
  253. * 1) getopt(3) returned an error.
  254. * 2) A problem with parameter parsing for getopt(1).
  255. * 3) Internal error, out of memory
  256. * 4) Returned for -T
  257. */
  258. static struct option longopts[]=
  259. {
  260. {"options",required_argument,NULL,'o'},
  261. {"longoptions",required_argument,NULL,'l'},
  262. {"quiet",no_argument,NULL,'q'},
  263. {"quiet-output",no_argument,NULL,'Q'},
  264. {"shell",required_argument,NULL,'s'},
  265. {"test",no_argument,NULL,'T'},
  266. {"unquoted",no_argument,NULL,'u'},
  267. {"alternative",no_argument,NULL,'a'},
  268. {"name",required_argument,NULL,'n'},
  269. {NULL,0,NULL,0}
  270. };
  271. /* Stop scanning as soon as a non-option argument is found! */
  272. static const char *shortopts="+ao:l:n:qQs:Tu";
  273. int getopt_main(int argc, char *argv[])
  274. {
  275. const char *optstr = NULL;
  276. const char *name = NULL;
  277. int opt;
  278. int compatible=0;
  279. init_longopt();
  280. if (getenv("GETOPT_COMPATIBLE"))
  281. compatible=1;
  282. if (argc == 1) {
  283. if (compatible) {
  284. /* For some reason, the original getopt gave no error
  285. when there were no arguments. */
  286. printf(" --\n");
  287. return 0;
  288. } else
  289. bb_error_msg_and_die("missing optstring argument");
  290. }
  291. if (argv[1][0] != '-' || compatible) {
  292. quote=0;
  293. optstr=xmalloc(strlen(argv[1])+1);
  294. strcpy(optstr,argv[1]+strspn(argv[1],"-+"));
  295. argv[1]=argv[0];
  296. return (generate_output(argv+1,argc-1,optstr,long_options));
  297. }
  298. while ((opt=getopt_long(argc,argv,shortopts,longopts,NULL)) != EOF)
  299. switch (opt) {
  300. case 'a':
  301. alternative=1;
  302. break;
  303. case 'o':
  304. free(optstr);
  305. optstr = optarg;
  306. break;
  307. case 'l':
  308. add_long_options(optarg);
  309. break;
  310. case 'n':
  311. free(name);
  312. name = optarg;
  313. break;
  314. case 'q':
  315. quiet_errors=1;
  316. break;
  317. case 'Q':
  318. quiet_output=1;
  319. break;
  320. case 's':
  321. set_shell(optarg);
  322. break;
  323. case 'T':
  324. return 4;
  325. case 'u':
  326. quote=0;
  327. break;
  328. default:
  329. bb_show_usage();
  330. }
  331. if (!optstr) {
  332. if (optind >= argc)
  333. bb_error_msg_and_die("missing optstring argument");
  334. else {
  335. optstr=bb_xstrdup(argv[optind]);
  336. optind++;
  337. }
  338. }
  339. if (name)
  340. argv[optind-1]=name;
  341. else
  342. argv[optind-1]=argv[0];
  343. return (generate_output(argv+optind-1,argc-optind+1,optstr,long_options));
  344. }
  345. /*
  346. Local Variables:
  347. c-file-style: "linux"
  348. c-basic-offset: 4
  349. tab-width: 4
  350. End:
  351. */