PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/bin/sh/options.c

https://bitbucket.org/freebsd/freebsd-head/
C | 570 lines | 414 code | 58 blank | 98 comment | 199 complexity | da1538e882f2926a89b52b535c287f2c MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, BSD-3-Clause, LGPL-2.0, LGPL-2.1, BSD-2-Clause, 0BSD, JSON, AGPL-1.0, GPL-2.0
  1. /*-
  2. * Copyright (c) 1991, 1993
  3. * The Regents of the University of California. All rights reserved.
  4. *
  5. * This code is derived from software contributed to Berkeley by
  6. * Kenneth Almquist.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. * 4. Neither the name of the University nor the names of its contributors
  17. * may be used to endorse or promote products derived from this software
  18. * without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  21. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  23. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  24. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  26. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  27. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  28. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  29. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  30. * SUCH DAMAGE.
  31. */
  32. #ifndef lint
  33. #if 0
  34. static char sccsid[] = "@(#)options.c 8.2 (Berkeley) 5/4/95";
  35. #endif
  36. #endif /* not lint */
  37. #include <sys/cdefs.h>
  38. __FBSDID("$FreeBSD$");
  39. #include <signal.h>
  40. #include <unistd.h>
  41. #include <stdlib.h>
  42. #include "shell.h"
  43. #define DEFINE_OPTIONS
  44. #include "options.h"
  45. #undef DEFINE_OPTIONS
  46. #include "nodes.h" /* for other header files */
  47. #include "eval.h"
  48. #include "jobs.h"
  49. #include "input.h"
  50. #include "output.h"
  51. #include "trap.h"
  52. #include "var.h"
  53. #include "memalloc.h"
  54. #include "error.h"
  55. #include "mystring.h"
  56. #include "builtins.h"
  57. #ifndef NO_HISTORY
  58. #include "myhistedit.h"
  59. #endif
  60. char *arg0; /* value of $0 */
  61. struct shparam shellparam; /* current positional parameters */
  62. char **argptr; /* argument list for builtin commands */
  63. char *shoptarg; /* set by nextopt (like getopt) */
  64. char *nextopt_optptr; /* used by nextopt */
  65. char *minusc; /* argument to -c option */
  66. static void options(int);
  67. static void minus_o(char *, int);
  68. static void setoption(int, int);
  69. static int getopts(char *, char *, char **, char ***, char **);
  70. /*
  71. * Process the shell command line arguments.
  72. */
  73. void
  74. procargs(int argc, char **argv)
  75. {
  76. int i;
  77. char *scriptname;
  78. argptr = argv;
  79. if (argc > 0)
  80. argptr++;
  81. for (i = 0; i < NOPTS; i++)
  82. optlist[i].val = 2;
  83. privileged = (getuid() != geteuid() || getgid() != getegid());
  84. options(1);
  85. if (*argptr == NULL && minusc == NULL)
  86. sflag = 1;
  87. if (iflag != 0 && sflag == 1 && isatty(0) && isatty(1)) {
  88. iflag = 1;
  89. if (Eflag == 2)
  90. Eflag = 1;
  91. }
  92. if (mflag == 2)
  93. mflag = iflag;
  94. for (i = 0; i < NOPTS; i++)
  95. if (optlist[i].val == 2)
  96. optlist[i].val = 0;
  97. arg0 = argv[0];
  98. if (sflag == 0 && minusc == NULL) {
  99. scriptname = *argptr++;
  100. setinputfile(scriptname, 0);
  101. commandname = arg0 = scriptname;
  102. }
  103. /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
  104. if (argptr && minusc && *argptr)
  105. arg0 = *argptr++;
  106. shellparam.p = argptr;
  107. shellparam.reset = 1;
  108. /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
  109. while (*argptr) {
  110. shellparam.nparam++;
  111. argptr++;
  112. }
  113. optschanged();
  114. }
  115. void
  116. optschanged(void)
  117. {
  118. setinteractive(iflag);
  119. #ifndef NO_HISTORY
  120. histedit();
  121. #endif
  122. setjobctl(mflag);
  123. }
  124. /*
  125. * Process shell options. The global variable argptr contains a pointer
  126. * to the argument list; we advance it past the options.
  127. */
  128. static void
  129. options(int cmdline)
  130. {
  131. char *kp, *p;
  132. int val;
  133. int c;
  134. if (cmdline)
  135. minusc = NULL;
  136. while ((p = *argptr) != NULL) {
  137. argptr++;
  138. if ((c = *p++) == '-') {
  139. val = 1;
  140. /* A "-" or "--" terminates options */
  141. if (p[0] == '\0')
  142. goto end_options1;
  143. if (p[0] == '-' && p[1] == '\0')
  144. goto end_options2;
  145. /**
  146. * For the benefit of `#!' lines in shell scripts,
  147. * treat a string of '-- *#.*' the same as '--'.
  148. * This is needed so that a script starting with:
  149. * #!/bin/sh -- # -*- perl -*-
  150. * will continue to work after a change is made to
  151. * kern/imgact_shell.c to NOT token-ize the options
  152. * specified on a '#!' line. A bit of a kludge,
  153. * but that trick is recommended in documentation
  154. * for some scripting languages, and we might as
  155. * well continue to support it.
  156. */
  157. if (p[0] == '-') {
  158. kp = p + 1;
  159. while (*kp == ' ' || *kp == '\t')
  160. kp++;
  161. if (*kp == '#' || *kp == '\0')
  162. goto end_options2;
  163. }
  164. } else if (c == '+') {
  165. val = 0;
  166. } else {
  167. argptr--;
  168. break;
  169. }
  170. while ((c = *p++) != '\0') {
  171. if (c == 'c' && cmdline) {
  172. char *q;
  173. #ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */
  174. if (*p == '\0')
  175. #endif
  176. q = *argptr++;
  177. if (q == NULL || minusc != NULL)
  178. error("Bad -c option");
  179. minusc = q;
  180. #ifdef NOHACK
  181. break;
  182. #endif
  183. } else if (c == 'o') {
  184. minus_o(*argptr, val);
  185. if (*argptr)
  186. argptr++;
  187. } else
  188. setoption(c, val);
  189. }
  190. }
  191. return;
  192. /* When processing `set', a single "-" means turn off -x and -v */
  193. end_options1:
  194. if (!cmdline) {
  195. xflag = vflag = 0;
  196. return;
  197. }
  198. /*
  199. * When processing `set', a "--" means the remaining arguments
  200. * replace the positional parameters in the active shell. If
  201. * there are no remaining options, then all the positional
  202. * parameters are cleared (equivalent to doing ``shift $#'').
  203. */
  204. end_options2:
  205. if (!cmdline) {
  206. if (*argptr == NULL)
  207. setparam(argptr);
  208. return;
  209. }
  210. /*
  211. * At this point we are processing options given to 'sh' on a command
  212. * line. If an end-of-options marker ("-" or "--") is followed by an
  213. * arg of "#", then skip over all remaining arguments. Some scripting
  214. * languages (e.g.: perl) document that /bin/sh will implement this
  215. * behavior, and they recommend that users take advantage of it to
  216. * solve certain issues that can come up when writing a perl script.
  217. * Yes, this feature is in /bin/sh to help users write perl scripts.
  218. */
  219. p = *argptr;
  220. if (p != NULL && p[0] == '#' && p[1] == '\0') {
  221. while (*argptr != NULL)
  222. argptr++;
  223. /* We need to keep the final argument */
  224. argptr--;
  225. }
  226. }
  227. static void
  228. minus_o(char *name, int val)
  229. {
  230. int i;
  231. if (name == NULL) {
  232. if (val) {
  233. /* "Pretty" output. */
  234. out1str("Current option settings\n");
  235. for (i = 0; i < NOPTS; i++)
  236. out1fmt("%-16s%s\n", optlist[i].name,
  237. optlist[i].val ? "on" : "off");
  238. } else {
  239. /* Output suitable for re-input to shell. */
  240. for (i = 0; i < NOPTS; i++)
  241. out1fmt("%s %co %s%s",
  242. i % 6 == 0 ? "set" : "",
  243. optlist[i].val ? '-' : '+',
  244. optlist[i].name,
  245. i % 6 == 5 || i == NOPTS - 1 ? "\n" : "");
  246. }
  247. } else {
  248. for (i = 0; i < NOPTS; i++)
  249. if (equal(name, optlist[i].name)) {
  250. setoption(optlist[i].letter, val);
  251. return;
  252. }
  253. error("Illegal option -o %s", name);
  254. }
  255. }
  256. static void
  257. setoption(int flag, int val)
  258. {
  259. int i;
  260. if (flag == 'p' && !val && privileged) {
  261. if (setgid(getgid()) == -1)
  262. error("setgid");
  263. if (setuid(getuid()) == -1)
  264. error("setuid");
  265. }
  266. for (i = 0; i < NOPTS; i++)
  267. if (optlist[i].letter == flag) {
  268. optlist[i].val = val;
  269. if (val) {
  270. /* #%$ hack for ksh semantics */
  271. if (flag == 'V')
  272. Eflag = 0;
  273. else if (flag == 'E')
  274. Vflag = 0;
  275. }
  276. return;
  277. }
  278. error("Illegal option -%c", flag);
  279. }
  280. /*
  281. * Set the shell parameters.
  282. */
  283. void
  284. setparam(char **argv)
  285. {
  286. char **newparam;
  287. char **ap;
  288. int nparam;
  289. for (nparam = 0 ; argv[nparam] ; nparam++);
  290. ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
  291. while (*argv) {
  292. *ap++ = savestr(*argv++);
  293. }
  294. *ap = NULL;
  295. freeparam(&shellparam);
  296. shellparam.malloc = 1;
  297. shellparam.nparam = nparam;
  298. shellparam.p = newparam;
  299. shellparam.reset = 1;
  300. shellparam.optnext = NULL;
  301. }
  302. /*
  303. * Free the list of positional parameters.
  304. */
  305. void
  306. freeparam(struct shparam *param)
  307. {
  308. char **ap;
  309. if (param->malloc) {
  310. for (ap = param->p ; *ap ; ap++)
  311. ckfree(*ap);
  312. ckfree(param->p);
  313. }
  314. }
  315. /*
  316. * The shift builtin command.
  317. */
  318. int
  319. shiftcmd(int argc, char **argv)
  320. {
  321. int n;
  322. char **ap1, **ap2;
  323. n = 1;
  324. if (argc > 1)
  325. n = number(argv[1]);
  326. if (n > shellparam.nparam)
  327. return 1;
  328. INTOFF;
  329. shellparam.nparam -= n;
  330. for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
  331. if (shellparam.malloc)
  332. ckfree(*ap1);
  333. }
  334. ap2 = shellparam.p;
  335. while ((*ap2++ = *ap1++) != NULL);
  336. shellparam.reset = 1;
  337. INTON;
  338. return 0;
  339. }
  340. /*
  341. * The set command builtin.
  342. */
  343. int
  344. setcmd(int argc, char **argv)
  345. {
  346. if (argc == 1)
  347. return showvarscmd(argc, argv);
  348. INTOFF;
  349. options(0);
  350. optschanged();
  351. if (*argptr != NULL) {
  352. setparam(argptr);
  353. }
  354. INTON;
  355. return 0;
  356. }
  357. void
  358. getoptsreset(const char *value)
  359. {
  360. while (*value == '0')
  361. value++;
  362. if (strcmp(value, "1") == 0)
  363. shellparam.reset = 1;
  364. }
  365. /*
  366. * The getopts builtin. Shellparam.optnext points to the next argument
  367. * to be processed. Shellparam.optptr points to the next character to
  368. * be processed in the current argument. If shellparam.optnext is NULL,
  369. * then it's the first time getopts has been called.
  370. */
  371. int
  372. getoptscmd(int argc, char **argv)
  373. {
  374. char **optbase = NULL;
  375. if (argc < 3)
  376. error("usage: getopts optstring var [arg]");
  377. else if (argc == 3)
  378. optbase = shellparam.p;
  379. else
  380. optbase = &argv[3];
  381. if (shellparam.reset == 1) {
  382. shellparam.optnext = optbase;
  383. shellparam.optptr = NULL;
  384. shellparam.reset = 0;
  385. }
  386. return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
  387. &shellparam.optptr);
  388. }
  389. static int
  390. getopts(char *optstr, char *optvar, char **optfirst, char ***optnext,
  391. char **optptr)
  392. {
  393. char *p, *q;
  394. char c = '?';
  395. int done = 0;
  396. int ind = 0;
  397. int err = 0;
  398. char s[10];
  399. if ((p = *optptr) == NULL || *p == '\0') {
  400. /* Current word is done, advance */
  401. if (*optnext == NULL)
  402. return 1;
  403. p = **optnext;
  404. if (p == NULL || *p != '-' || *++p == '\0') {
  405. atend:
  406. ind = *optnext - optfirst + 1;
  407. *optnext = NULL;
  408. p = NULL;
  409. done = 1;
  410. goto out;
  411. }
  412. (*optnext)++;
  413. if (p[0] == '-' && p[1] == '\0') /* check for "--" */
  414. goto atend;
  415. }
  416. c = *p++;
  417. for (q = optstr; *q != c; ) {
  418. if (*q == '\0') {
  419. if (optstr[0] == ':') {
  420. s[0] = c;
  421. s[1] = '\0';
  422. err |= setvarsafe("OPTARG", s, 0);
  423. }
  424. else {
  425. out1fmt("Illegal option -%c\n", c);
  426. (void) unsetvar("OPTARG");
  427. }
  428. c = '?';
  429. goto bad;
  430. }
  431. if (*++q == ':')
  432. q++;
  433. }
  434. if (*++q == ':') {
  435. if (*p == '\0' && (p = **optnext) == NULL) {
  436. if (optstr[0] == ':') {
  437. s[0] = c;
  438. s[1] = '\0';
  439. err |= setvarsafe("OPTARG", s, 0);
  440. c = ':';
  441. }
  442. else {
  443. out1fmt("No arg for -%c option\n", c);
  444. (void) unsetvar("OPTARG");
  445. c = '?';
  446. }
  447. goto bad;
  448. }
  449. if (p == **optnext)
  450. (*optnext)++;
  451. setvarsafe("OPTARG", p, 0);
  452. p = NULL;
  453. }
  454. else
  455. setvarsafe("OPTARG", "", 0);
  456. ind = *optnext - optfirst + 1;
  457. goto out;
  458. bad:
  459. ind = 1;
  460. *optnext = NULL;
  461. p = NULL;
  462. out:
  463. *optptr = p;
  464. fmtstr(s, sizeof(s), "%d", ind);
  465. err |= setvarsafe("OPTIND", s, VNOFUNC);
  466. s[0] = c;
  467. s[1] = '\0';
  468. err |= setvarsafe(optvar, s, 0);
  469. if (err) {
  470. *optnext = NULL;
  471. *optptr = NULL;
  472. flushall();
  473. exraise(EXERROR);
  474. }
  475. return done;
  476. }
  477. /*
  478. * Standard option processing (a la getopt) for builtin routines. The
  479. * only argument that is passed to nextopt is the option string; the
  480. * other arguments are unnecessary. It return the character, or '\0' on
  481. * end of input.
  482. */
  483. int
  484. nextopt(const char *optstring)
  485. {
  486. char *p;
  487. const char *q;
  488. char c;
  489. if ((p = nextopt_optptr) == NULL || *p == '\0') {
  490. p = *argptr;
  491. if (p == NULL || *p != '-' || *++p == '\0')
  492. return '\0';
  493. argptr++;
  494. if (p[0] == '-' && p[1] == '\0') /* check for "--" */
  495. return '\0';
  496. }
  497. c = *p++;
  498. for (q = optstring ; *q != c ; ) {
  499. if (*q == '\0')
  500. error("Illegal option -%c", c);
  501. if (*++q == ':')
  502. q++;
  503. }
  504. if (*++q == ':') {
  505. if (*p == '\0' && (p = *argptr++) == NULL)
  506. error("No arg for -%c option", c);
  507. shoptarg = p;
  508. p = NULL;
  509. }
  510. nextopt_optptr = p;
  511. return c;
  512. }