/contrib/tcsh/sh.dol.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 1115 lines · 1017 code · 25 blank · 73 comment · 40 complexity · cc787f9a6c7e6928a609640845cac01b MD5 · raw file

  1. /* $Header: /p/tcsh/cvsroot/tcsh/sh.dol.c,v 3.83 2011/01/25 20:10:46 christos Exp $ */
  2. /*
  3. * sh.dol.c: Variable substitutions
  4. */
  5. /*-
  6. * Copyright (c) 1980, 1991 The Regents of the University of California.
  7. * All rights reserved.
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions
  11. * are met:
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in the
  16. * documentation and/or other materials provided with the distribution.
  17. * 3. Neither the name of the University nor the names of its contributors
  18. * may be used to endorse or promote products derived from this software
  19. * without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31. * SUCH DAMAGE.
  32. */
  33. #include "sh.h"
  34. RCSID("$tcsh: sh.dol.c,v 3.83 2011/01/25 20:10:46 christos Exp $")
  35. /*
  36. * C shell
  37. */
  38. /*
  39. * These routines perform variable substitution and quoting via ' and ".
  40. * To this point these constructs have been preserved in the divided
  41. * input words. Here we expand variables and turn quoting via ' and " into
  42. * QUOTE bits on characters (which prevent further interpretation).
  43. * If the `:q' modifier was applied during history expansion, then
  44. * some QUOTEing may have occurred already, so we dont "trim()" here.
  45. */
  46. static eChar Dpeekc; /* Peek for DgetC */
  47. static eChar Dpeekrd; /* Peek for Dreadc */
  48. static Char *Dcp, *const *Dvp; /* Input vector for Dreadc */
  49. #define DEOF CHAR_ERR
  50. #define unDgetC(c) Dpeekc = c
  51. #define QUOTES (_QF|_QB|_ESC) /* \ ' " ` */
  52. /*
  53. * The following variables give the information about the current
  54. * $ expansion, recording the current word position, the remaining
  55. * words within this expansion, the count of remaining words, and the
  56. * information about any : modifier which is being applied.
  57. */
  58. static Char *dolp; /* Remaining chars from this word */
  59. static Char **dolnxt; /* Further words */
  60. static int dolcnt; /* Count of further words */
  61. static struct Strbuf dolmod; /* = Strbuf_INIT; : modifier characters */
  62. static int dolmcnt; /* :gx -> INT_MAX, else 1 */
  63. static int dol_flag_a; /* :ax -> 1, else 0 */
  64. static Char **Dfix2 (Char *const *);
  65. static int Dpack (struct Strbuf *);
  66. static int Dword (struct blk_buf *);
  67. static void dolerror (Char *);
  68. static eChar DgetC (int);
  69. static void Dgetdol (void);
  70. static void fixDolMod (void);
  71. static void setDolp (Char *);
  72. static void unDredc (eChar);
  73. static eChar Dredc (void);
  74. static void Dtestq (Char);
  75. /*
  76. * Fix up the $ expansions and quotations in the
  77. * argument list to command t.
  78. */
  79. void
  80. Dfix(struct command *t)
  81. {
  82. Char **pp;
  83. Char *p;
  84. if (noexec)
  85. return;
  86. /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */
  87. for (pp = t->t_dcom; (p = *pp++) != NULL;) {
  88. for (; *p; p++) {
  89. if (cmap(*p, _DOL | QUOTES)) { /* $, \, ', ", ` */
  90. Char **expanded;
  91. expanded = Dfix2(t->t_dcom); /* found one */
  92. blkfree(t->t_dcom);
  93. t->t_dcom = expanded;
  94. return;
  95. }
  96. }
  97. }
  98. }
  99. /*
  100. * $ substitute one word, for i/o redirection
  101. */
  102. Char *
  103. Dfix1(Char *cp)
  104. {
  105. Char *Dv[2], **expanded;
  106. if (noexec)
  107. return (0);
  108. Dv[0] = cp;
  109. Dv[1] = NULL;
  110. expanded = Dfix2(Dv);
  111. if (expanded[0] == NULL || expanded[1] != NULL) {
  112. blkfree(expanded);
  113. setname(short2str(cp));
  114. stderror(ERR_NAME | ERR_AMBIG);
  115. }
  116. cp = Strsave(expanded[0]);
  117. blkfree(expanded);
  118. return (cp);
  119. }
  120. /*
  121. * Subroutine to do actual fixing after state initialization.
  122. */
  123. static Char **
  124. Dfix2(Char *const *v)
  125. {
  126. struct blk_buf *bb = bb_alloc();
  127. Char **vec;
  128. Dvp = v;
  129. Dcp = STRNULL; /* Setup input vector for Dreadc */
  130. unDgetC(0);
  131. unDredc(0); /* Clear out any old peeks (at error) */
  132. dolp = 0;
  133. dolcnt = 0; /* Clear out residual $ expands (...) */
  134. cleanup_push(bb, bb_free);
  135. while (Dword(bb))
  136. continue;
  137. cleanup_ignore(bb);
  138. cleanup_until(bb);
  139. vec = bb_finish(bb);
  140. xfree(bb);
  141. return vec;
  142. }
  143. /*
  144. * Pack up more characters in this word
  145. */
  146. static int
  147. Dpack(struct Strbuf *wbuf)
  148. {
  149. eChar c;
  150. for (;;) {
  151. c = DgetC(DODOL);
  152. if (c == '\\') {
  153. c = DgetC(0);
  154. if (c == DEOF) {
  155. unDredc(c);
  156. return 1;
  157. }
  158. if (c == '\n')
  159. c = ' ';
  160. else
  161. c |= QUOTE;
  162. }
  163. if (c == DEOF) {
  164. unDredc(c);
  165. return 1;
  166. }
  167. if (cmap(c, _SP | _NL | _QF | _QB)) { /* sp \t\n'"` */
  168. unDgetC(c);
  169. if (cmap(c, QUOTES))
  170. return 0;
  171. return 1;
  172. }
  173. Strbuf_append1(wbuf, (Char) c);
  174. }
  175. }
  176. /*
  177. * Get a word. This routine is analogous to the routine
  178. * word() in sh.lex.c for the main lexical input. One difference
  179. * here is that we don't get a newline to terminate our expansion.
  180. * Rather, DgetC will return a DEOF when we hit the end-of-input.
  181. */
  182. static int
  183. Dword(struct blk_buf *bb)
  184. {
  185. eChar c, c1;
  186. struct Strbuf *wbuf = Strbuf_alloc();
  187. int dolflg;
  188. int sofar = 0;
  189. Char *str;
  190. cleanup_push(wbuf, Strbuf_free);
  191. for (;;) {
  192. c = DgetC(DODOL);
  193. switch (c) {
  194. case DEOF:
  195. if (sofar == 0) {
  196. cleanup_until(wbuf);
  197. return (0);
  198. }
  199. /* finish this word and catch the code above the next time */
  200. unDredc(c);
  201. /*FALLTHROUGH*/
  202. case '\n':
  203. goto end;
  204. case ' ':
  205. case '\t':
  206. continue;
  207. case '`':
  208. /* We preserve ` quotations which are done yet later */
  209. Strbuf_append1(wbuf, (Char) c);
  210. /*FALLTHROUGH*/
  211. case '\'':
  212. case '"':
  213. /*
  214. * Note that DgetC never returns a QUOTES character from an
  215. * expansion, so only true input quotes will get us here or out.
  216. */
  217. c1 = c;
  218. dolflg = c1 == '"' ? DODOL : 0;
  219. for (;;) {
  220. c = DgetC(dolflg);
  221. if (c == c1)
  222. break;
  223. if (c == '\n' || c == DEOF) {
  224. cleanup_until(bb);
  225. stderror(ERR_UNMATCHED, (int)c1);
  226. }
  227. if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE)) {
  228. if (wbuf->len != 0 && (wbuf->s[wbuf->len - 1] & TRIM) == '\\')
  229. wbuf->len--;
  230. }
  231. switch (c1) {
  232. case '"':
  233. /*
  234. * Leave any `s alone for later. Other chars are all
  235. * quoted, thus `...` can tell it was within "...".
  236. */
  237. Strbuf_append1(wbuf, c == '`' ? '`' : c | QUOTE);
  238. break;
  239. case '\'':
  240. /* Prevent all further interpretation */
  241. Strbuf_append1(wbuf, c | QUOTE);
  242. break;
  243. case '`':
  244. /* Leave all text alone for later */
  245. Strbuf_append1(wbuf, (Char) c);
  246. break;
  247. default:
  248. break;
  249. }
  250. }
  251. if (c1 == '`')
  252. Strbuf_append1(wbuf, '`');
  253. sofar = 1;
  254. if (Dpack(wbuf) != 0)
  255. goto end;
  256. continue;
  257. case '\\':
  258. c = DgetC(0); /* No $ subst! */
  259. if (c == '\n' || c == DEOF)
  260. continue;
  261. c |= QUOTE;
  262. break;
  263. default:
  264. break;
  265. }
  266. unDgetC(c);
  267. sofar = 1;
  268. if (Dpack(wbuf) != 0)
  269. goto end;
  270. }
  271. end:
  272. cleanup_ignore(wbuf);
  273. cleanup_until(wbuf);
  274. str = Strbuf_finish(wbuf);
  275. bb_append(bb, str);
  276. xfree(wbuf);
  277. return 1;
  278. }
  279. /*
  280. * Get a character, performing $ substitution unless flag is 0.
  281. * Any QUOTES character which is returned from a $ expansion is
  282. * QUOTEd so that it will not be recognized above.
  283. */
  284. static eChar
  285. DgetC(int flag)
  286. {
  287. eChar c;
  288. top:
  289. if ((c = Dpeekc) != 0) {
  290. Dpeekc = 0;
  291. return (c);
  292. }
  293. if (lap < labuf.len) {
  294. c = labuf.s[lap++] & (QUOTE | TRIM);
  295. quotspec:
  296. if (cmap(c, QUOTES))
  297. return (c | QUOTE);
  298. return (c);
  299. }
  300. if (dolp) {
  301. if ((c = *dolp++ & (QUOTE | TRIM)) != 0)
  302. goto quotspec;
  303. if (dolcnt > 0) {
  304. setDolp(*dolnxt++);
  305. --dolcnt;
  306. return (' ');
  307. }
  308. dolp = 0;
  309. }
  310. if (dolcnt > 0) {
  311. setDolp(*dolnxt++);
  312. --dolcnt;
  313. goto top;
  314. }
  315. c = Dredc();
  316. if (c == '$' && flag) {
  317. Dgetdol();
  318. goto top;
  319. }
  320. return (c);
  321. }
  322. static Char *nulvec[] = { NULL };
  323. static struct varent nulargv = {nulvec, STRargv, VAR_READWRITE,
  324. { NULL, NULL, NULL }, 0 };
  325. static void
  326. dolerror(Char *s)
  327. {
  328. setname(short2str(s));
  329. stderror(ERR_NAME | ERR_RANGE);
  330. }
  331. /*
  332. * Handle the multitudinous $ expansion forms.
  333. * Ugh.
  334. */
  335. static void
  336. Dgetdol(void)
  337. {
  338. Char *np;
  339. struct varent *vp = NULL;
  340. struct Strbuf *name = Strbuf_alloc();
  341. eChar c, sc;
  342. int subscr = 0, lwb = 1, upb = 0;
  343. int dimen = 0, bitset = 0, length = 0;
  344. static Char *dolbang = NULL;
  345. cleanup_push(name, Strbuf_free);
  346. dolmod.len = dolmcnt = dol_flag_a = 0;
  347. c = sc = DgetC(0);
  348. if (c == DEOF) {
  349. stderror(ERR_SYNTAX);
  350. return;
  351. }
  352. if (c == '{')
  353. c = DgetC(0); /* sc is { to take } later */
  354. if ((c & TRIM) == '#')
  355. dimen++, c = DgetC(0); /* $# takes dimension */
  356. else if (c == '?')
  357. bitset++, c = DgetC(0); /* $? tests existence */
  358. else if (c == '%')
  359. length++, c = DgetC(0); /* $% returns length in chars */
  360. switch (c) {
  361. case '!':
  362. if (dimen || bitset || length)
  363. stderror(ERR_SYNTAX);
  364. if (backpid != 0) {
  365. xfree(dolbang);
  366. setDolp(dolbang = putn((tcsh_number_t)backpid));
  367. }
  368. cleanup_until(name);
  369. goto eatbrac;
  370. case '$':
  371. if (dimen || bitset || length)
  372. stderror(ERR_SYNTAX);
  373. setDolp(doldol);
  374. cleanup_until(name);
  375. goto eatbrac;
  376. case '<'|QUOTE: {
  377. static struct Strbuf wbuf; /* = Strbuf_INIT; */
  378. if (bitset)
  379. stderror(ERR_NOTALLOWED, "$?<");
  380. if (dimen)
  381. stderror(ERR_NOTALLOWED, "$#<");
  382. if (length)
  383. stderror(ERR_NOTALLOWED, "$%<");
  384. wbuf.len = 0;
  385. {
  386. char cbuf[MB_LEN_MAX];
  387. size_t cbp = 0;
  388. int old_pintr_disabled;
  389. for (;;) {
  390. int len;
  391. ssize_t res;
  392. Char wc;
  393. pintr_push_enable(&old_pintr_disabled);
  394. res = force_read(OLDSTD, cbuf + cbp, 1);
  395. cleanup_until(&old_pintr_disabled);
  396. if (res != 1)
  397. break;
  398. cbp++;
  399. len = normal_mbtowc(&wc, cbuf, cbp);
  400. if (len == -1) {
  401. reset_mbtowc();
  402. if (cbp < MB_LEN_MAX)
  403. continue; /* Maybe a partial character */
  404. wc = (unsigned char)*cbuf | INVALID_BYTE;
  405. }
  406. if (len <= 0)
  407. len = 1;
  408. if (cbp != (size_t)len)
  409. memmove(cbuf, cbuf + len, cbp - len);
  410. cbp -= len;
  411. if (wc == '\n')
  412. break;
  413. Strbuf_append1(&wbuf, wc);
  414. }
  415. while (cbp != 0) {
  416. int len;
  417. Char wc;
  418. len = normal_mbtowc(&wc, cbuf, cbp);
  419. if (len == -1) {
  420. reset_mbtowc();
  421. wc = (unsigned char)*cbuf | INVALID_BYTE;
  422. }
  423. if (len <= 0)
  424. len = 1;
  425. if (cbp != (size_t)len)
  426. memmove(cbuf, cbuf + len, cbp - len);
  427. cbp -= len;
  428. if (wc == '\n')
  429. break;
  430. Strbuf_append1(&wbuf, wc);
  431. }
  432. Strbuf_terminate(&wbuf);
  433. }
  434. fixDolMod();
  435. setDolp(wbuf.s); /* Kept allocated until next $< expansion */
  436. cleanup_until(name);
  437. goto eatbrac;
  438. }
  439. case '*':
  440. Strbuf_append(name, STRargv);
  441. Strbuf_terminate(name);
  442. vp = adrof(STRargv);
  443. subscr = -1; /* Prevent eating [...] */
  444. break;
  445. case DEOF:
  446. case '\n':
  447. np = dimen ? STRargv : (bitset ? STRstatus : NULL);
  448. if (np) {
  449. bitset = 0;
  450. Strbuf_append(name, np);
  451. Strbuf_terminate(name);
  452. vp = adrof(np);
  453. subscr = -1; /* Prevent eating [...] */
  454. unDredc(c);
  455. break;
  456. }
  457. else
  458. stderror(ERR_SYNTAX);
  459. /*NOTREACHED*/
  460. default:
  461. if (Isdigit(c)) {
  462. if (dimen)
  463. stderror(ERR_NOTALLOWED, "$#<num>");
  464. subscr = 0;
  465. do {
  466. subscr = subscr * 10 + c - '0';
  467. c = DgetC(0);
  468. } while (c != DEOF && Isdigit(c));
  469. unDredc(c);
  470. if (subscr < 0)
  471. stderror(ERR_RANGE);
  472. if (subscr == 0) {
  473. if (bitset) {
  474. dolp = dolzero ? STR1 : STR0;
  475. cleanup_until(name);
  476. goto eatbrac;
  477. }
  478. if (ffile == 0)
  479. stderror(ERR_DOLZERO);
  480. if (length) {
  481. length = Strlen(ffile);
  482. addla(putn((tcsh_number_t)length));
  483. }
  484. else {
  485. fixDolMod();
  486. setDolp(ffile);
  487. }
  488. cleanup_until(name);
  489. goto eatbrac;
  490. }
  491. #if 0
  492. if (bitset)
  493. stderror(ERR_NOTALLOWED, "$?<num>");
  494. if (length)
  495. stderror(ERR_NOTALLOWED, "$%<num>");
  496. #endif
  497. vp = adrof(STRargv);
  498. if (vp == 0) {
  499. vp = &nulargv;
  500. cleanup_until(name);
  501. goto eatmod;
  502. }
  503. break;
  504. }
  505. if (c == DEOF || !alnum(c)) {
  506. np = dimen ? STRargv : (bitset ? STRstatus : NULL);
  507. if (np) {
  508. bitset = 0;
  509. Strbuf_append(name, np);
  510. Strbuf_terminate(name);
  511. vp = adrof(np);
  512. subscr = -1; /* Prevent eating [...] */
  513. unDredc(c);
  514. break;
  515. }
  516. else
  517. stderror(ERR_VARALNUM);
  518. }
  519. for (;;) {
  520. Strbuf_append1(name, (Char) c);
  521. c = DgetC(0);
  522. if (c == DEOF || !alnum(c))
  523. break;
  524. }
  525. Strbuf_terminate(name);
  526. unDredc(c);
  527. vp = adrof(name->s);
  528. }
  529. if (bitset) {
  530. dolp = (vp || getenv(short2str(name->s))) ? STR1 : STR0;
  531. cleanup_until(name);
  532. goto eatbrac;
  533. }
  534. if (vp == NULL || vp->vec == NULL) {
  535. np = str2short(getenv(short2str(name->s)));
  536. if (np) {
  537. static Char *env_val; /* = NULL; */
  538. cleanup_until(name);
  539. fixDolMod();
  540. if (length) {
  541. addla(putn((tcsh_number_t)Strlen(np)));
  542. } else {
  543. xfree(env_val);
  544. env_val = Strsave(np);
  545. setDolp(env_val);
  546. }
  547. goto eatbrac;
  548. }
  549. udvar(name->s);
  550. /* NOTREACHED */
  551. }
  552. cleanup_until(name);
  553. c = DgetC(0);
  554. upb = blklen(vp->vec);
  555. if (dimen == 0 && subscr == 0 && c == '[') {
  556. name = Strbuf_alloc();
  557. cleanup_push(name, Strbuf_free);
  558. np = name->s;
  559. for (;;) {
  560. c = DgetC(DODOL); /* Allow $ expand within [ ] */
  561. if (c == ']')
  562. break;
  563. if (c == '\n' || c == DEOF)
  564. stderror(ERR_INCBR);
  565. Strbuf_append1(name, (Char) c);
  566. }
  567. Strbuf_terminate(name);
  568. np = name->s;
  569. if (dolp || dolcnt) /* $ exp must end before ] */
  570. stderror(ERR_EXPORD);
  571. if (!*np)
  572. stderror(ERR_SYNTAX);
  573. if (Isdigit(*np)) {
  574. int i;
  575. for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0')
  576. continue;
  577. if (i < 0 || (i > upb && !any("-*", *np))) {
  578. cleanup_until(name);
  579. dolerror(vp->v_name);
  580. return;
  581. }
  582. lwb = i;
  583. if (!*np)
  584. upb = lwb, np = STRstar;
  585. }
  586. if (*np == '*')
  587. np++;
  588. else if (*np != '-')
  589. stderror(ERR_MISSING, '-');
  590. else {
  591. int i = upb;
  592. np++;
  593. if (Isdigit(*np)) {
  594. i = 0;
  595. while (Isdigit(*np))
  596. i = i * 10 + *np++ - '0';
  597. if (i < 0 || i > upb) {
  598. cleanup_until(name);
  599. dolerror(vp->v_name);
  600. return;
  601. }
  602. }
  603. if (i < lwb)
  604. upb = lwb - 1;
  605. else
  606. upb = i;
  607. }
  608. if (lwb == 0) {
  609. if (upb != 0) {
  610. cleanup_until(name);
  611. dolerror(vp->v_name);
  612. return;
  613. }
  614. upb = -1;
  615. }
  616. if (*np)
  617. stderror(ERR_SYNTAX);
  618. cleanup_until(name);
  619. }
  620. else {
  621. if (subscr > 0) {
  622. if (subscr > upb)
  623. lwb = 1, upb = 0;
  624. else
  625. lwb = upb = subscr;
  626. }
  627. unDredc(c);
  628. }
  629. if (dimen) {
  630. /* this is a kludge. It prevents Dgetdol() from */
  631. /* pushing erroneous ${#<error> values into the labuf. */
  632. if (sc == '{') {
  633. c = Dredc();
  634. if (c != '}')
  635. stderror(ERR_MISSING, '}');
  636. unDredc(c);
  637. }
  638. addla(putn((tcsh_number_t)(upb - lwb + 1)));
  639. }
  640. else if (length) {
  641. int i;
  642. for (i = lwb - 1, length = 0; i < upb; i++)
  643. length += Strlen(vp->vec[i]);
  644. #ifdef notdef
  645. /* We don't want that, since we can always compute it by adding $#xxx */
  646. length += i - 1; /* Add the number of spaces in */
  647. #endif
  648. addla(putn((tcsh_number_t)length));
  649. }
  650. else {
  651. eatmod:
  652. fixDolMod();
  653. dolnxt = &vp->vec[lwb - 1];
  654. dolcnt = upb - lwb + 1;
  655. }
  656. eatbrac:
  657. if (sc == '{') {
  658. c = Dredc();
  659. if (c != '}')
  660. stderror(ERR_MISSING, '}');
  661. }
  662. }
  663. static void
  664. fixDolMod(void)
  665. {
  666. eChar c;
  667. c = DgetC(0);
  668. if (c == ':') {
  669. do {
  670. c = DgetC(0), dolmcnt = 1, dol_flag_a = 0;
  671. if (c == 'g' || c == 'a') {
  672. if (c == 'g')
  673. dolmcnt = INT_MAX;
  674. else
  675. dol_flag_a = 1;
  676. c = DgetC(0);
  677. }
  678. if ((c == 'g' && dolmcnt != INT_MAX) ||
  679. (c == 'a' && dol_flag_a == 0)) {
  680. if (c == 'g')
  681. dolmcnt = INT_MAX;
  682. else
  683. dol_flag_a = 1;
  684. c = DgetC(0);
  685. }
  686. if (c == 's') { /* [eichin:19910926.0755EST] */
  687. int delimcnt = 2;
  688. eChar delim = DgetC(0);
  689. Strbuf_append1(&dolmod, (Char) c);
  690. Strbuf_append1(&dolmod, (Char) delim);
  691. if (delim == DEOF || !delim || letter(delim)
  692. || Isdigit(delim) || any(" \t\n", delim)) {
  693. seterror(ERR_BADSUBST);
  694. break;
  695. }
  696. while ((c = DgetC(0)) != DEOF) {
  697. Strbuf_append1(&dolmod, (Char) c);
  698. if(c == delim) delimcnt--;
  699. if(!delimcnt) break;
  700. }
  701. if(delimcnt) {
  702. seterror(ERR_BADSUBST);
  703. break;
  704. }
  705. continue;
  706. }
  707. if (!any("luhtrqxes", c))
  708. stderror(ERR_BADMOD, (int)c);
  709. Strbuf_append1(&dolmod, (Char) c);
  710. if (c == 'q')
  711. dolmcnt = INT_MAX;
  712. }
  713. while ((c = DgetC(0)) == ':');
  714. unDredc(c);
  715. }
  716. else
  717. unDredc(c);
  718. }
  719. static void
  720. setDolp(Char *cp)
  721. {
  722. Char *dp;
  723. size_t i;
  724. if (dolmod.len == 0 || dolmcnt == 0) {
  725. dolp = cp;
  726. return;
  727. }
  728. cp = Strsave(cp);
  729. for (i = 0; i < dolmod.len; i++) {
  730. int didmod = 0;
  731. /* handle s// [eichin:19910926.0510EST] */
  732. if(dolmod.s[i] == 's') {
  733. Char delim;
  734. Char *lhsub, *rhsub, *np;
  735. size_t lhlen = 0, rhlen = 0;
  736. delim = dolmod.s[++i];
  737. if (!delim || letter(delim)
  738. || Isdigit(delim) || any(" \t\n", delim)) {
  739. seterror(ERR_BADSUBST);
  740. break;
  741. }
  742. lhsub = &dolmod.s[++i];
  743. while(dolmod.s[i] != delim && dolmod.s[++i]) {
  744. lhlen++;
  745. }
  746. dolmod.s[i] = 0;
  747. rhsub = &dolmod.s[++i];
  748. while(dolmod.s[i] != delim && dolmod.s[++i]) {
  749. rhlen++;
  750. }
  751. dolmod.s[i] = 0;
  752. strip(lhsub);
  753. strip(rhsub);
  754. strip(cp);
  755. dp = cp;
  756. do {
  757. dp = Strstr(dp, lhsub);
  758. if (dp) {
  759. ptrdiff_t diff = dp - cp;
  760. size_t len = (Strlen(cp) + 1 - lhlen + rhlen);
  761. np = xmalloc(len * sizeof(Char));
  762. (void) Strncpy(np, cp, diff);
  763. (void) Strcpy(np + diff, rhsub);
  764. (void) Strcpy(np + diff + rhlen, dp + lhlen);
  765. dp = np + diff + 1;
  766. xfree(cp);
  767. cp = np;
  768. cp[--len] = '\0';
  769. didmod = 1;
  770. if (diff >= (ssize_t)len)
  771. break;
  772. } else {
  773. /* should this do a seterror? */
  774. break;
  775. }
  776. }
  777. while (dol_flag_a != 0);
  778. /*
  779. * restore dolmod for additional words
  780. */
  781. dolmod.s[i] = rhsub[-1] = (Char) delim;
  782. } else {
  783. do {
  784. if ((dp = domod(cp, dolmod.s[i])) != NULL) {
  785. didmod = 1;
  786. if (Strcmp(cp, dp) == 0) {
  787. xfree(cp);
  788. cp = dp;
  789. break;
  790. }
  791. else {
  792. xfree(cp);
  793. cp = dp;
  794. }
  795. }
  796. else
  797. break;
  798. }
  799. while (dol_flag_a != 0);
  800. }
  801. if (didmod && dolmcnt != INT_MAX)
  802. dolmcnt--;
  803. #ifdef notdef
  804. else
  805. break;
  806. #endif
  807. }
  808. addla(cp);
  809. dolp = STRNULL;
  810. if (seterr)
  811. stderror(ERR_OLD);
  812. }
  813. static void
  814. unDredc(eChar c)
  815. {
  816. Dpeekrd = c;
  817. }
  818. static eChar
  819. Dredc(void)
  820. {
  821. eChar c;
  822. if ((c = Dpeekrd) != 0) {
  823. Dpeekrd = 0;
  824. return (c);
  825. }
  826. if (Dcp && (c = *Dcp++))
  827. return (c & (QUOTE | TRIM));
  828. if (*Dvp == 0) {
  829. Dcp = 0;
  830. return (DEOF);
  831. }
  832. Dcp = *Dvp++;
  833. return (' ');
  834. }
  835. static int gflag;
  836. static void
  837. Dtestq(Char c)
  838. {
  839. if (cmap(c, QUOTES))
  840. gflag = 1;
  841. }
  842. static void
  843. inheredoc_cleanup(void *dummy)
  844. {
  845. USE(dummy);
  846. inheredoc = 0;
  847. }
  848. /*
  849. * Form a shell temporary file (in unit 0) from the words
  850. * of the shell input up to EOF or a line the same as "term".
  851. * Unit 0 should have been closed before this call.
  852. */
  853. void
  854. heredoc(Char *term)
  855. {
  856. eChar c;
  857. Char *Dv[2];
  858. struct Strbuf lbuf = Strbuf_INIT, mbuf = Strbuf_INIT;
  859. Char obuf[BUFSIZE + 1];
  860. #define OBUF_END (obuf + sizeof(obuf) / sizeof (*obuf) - 1)
  861. Char *lbp, *obp, *mbp;
  862. Char **vp;
  863. int quoted;
  864. #ifdef HAVE_MKSTEMP
  865. char *tmp = short2str(shtemp);
  866. char *dot = strrchr(tmp, '.');
  867. if (!dot)
  868. stderror(ERR_NAME | ERR_NOMATCH);
  869. strcpy(dot, TMP_TEMPLATE);
  870. xclose(0);
  871. if (mkstemp(tmp) == -1)
  872. stderror(ERR_SYSTEM, tmp, strerror(errno));
  873. #else /* !HAVE_MKSTEMP */
  874. char *tmp;
  875. # ifndef WINNT_NATIVE
  876. struct timeval tv;
  877. again:
  878. # endif /* WINNT_NATIVE */
  879. tmp = short2str(shtemp);
  880. # if O_CREAT == 0
  881. if (xcreat(tmp, 0600) < 0)
  882. stderror(ERR_SYSTEM, tmp, strerror(errno));
  883. # endif
  884. xclose(0);
  885. if (xopen(tmp, O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY|O_LARGEFILE, 0600) ==
  886. -1) {
  887. int oerrno = errno;
  888. # ifndef WINNT_NATIVE
  889. if (errno == EEXIST) {
  890. if (unlink(tmp) == -1) {
  891. (void) gettimeofday(&tv, NULL);
  892. xfree(shtemp);
  893. mbp = putn((((tcsh_number_t)tv.tv_sec) ^
  894. ((tcsh_number_t)tv.tv_usec) ^
  895. ((tcsh_number_t)getpid())) & 0x00ffffff);
  896. shtemp = Strspl(STRtmpsh, mbp);
  897. xfree(mbp);
  898. }
  899. goto again;
  900. }
  901. # endif /* WINNT_NATIVE */
  902. (void) unlink(tmp);
  903. errno = oerrno;
  904. stderror(ERR_SYSTEM, tmp, strerror(errno));
  905. }
  906. #endif /* HAVE_MKSTEMP */
  907. (void) unlink(tmp); /* 0 0 inode! */
  908. Dv[0] = term;
  909. Dv[1] = NULL;
  910. gflag = 0;
  911. trim(Dv);
  912. rscan(Dv, Dtestq);
  913. quoted = gflag;
  914. obp = obuf;
  915. obuf[BUFSIZE] = 0;
  916. inheredoc = 1;
  917. cleanup_push(&inheredoc, inheredoc_cleanup);
  918. #ifdef WINNT_NATIVE
  919. __dup_stdin = 1;
  920. #endif /* WINNT_NATIVE */
  921. cleanup_push(&lbuf, Strbuf_cleanup);
  922. cleanup_push(&mbuf, Strbuf_cleanup);
  923. for (;;) {
  924. Char **words;
  925. /*
  926. * Read up a line
  927. */
  928. lbuf.len = 0;
  929. for (;;) {
  930. c = readc(1); /* 1 -> Want EOF returns */
  931. if (c == CHAR_ERR || c == '\n')
  932. break;
  933. if ((c &= TRIM) != 0)
  934. Strbuf_append1(&lbuf, (Char) c);
  935. }
  936. Strbuf_terminate(&lbuf);
  937. /* Catch EOF in the middle of a line. */
  938. if (c == CHAR_ERR && lbuf.len != 0)
  939. c = '\n';
  940. /*
  941. * Check for EOF or compare to terminator -- before expansion
  942. */
  943. if (c == CHAR_ERR || eq(lbuf.s, term))
  944. break;
  945. /*
  946. * If term was quoted or -n just pass it on
  947. */
  948. if (quoted || noexec) {
  949. Strbuf_append1(&lbuf, '\n');
  950. Strbuf_terminate(&lbuf);
  951. for (lbp = lbuf.s; (c = *lbp++) != 0;) {
  952. *obp++ = (Char) c;
  953. if (obp == OBUF_END) {
  954. tmp = short2str(obuf);
  955. (void) xwrite(0, tmp, strlen (tmp));
  956. obp = obuf;
  957. }
  958. }
  959. continue;
  960. }
  961. /*
  962. * Term wasn't quoted so variable and then command expand the input
  963. * line
  964. */
  965. Dcp = lbuf.s;
  966. Dvp = Dv + 1;
  967. mbuf.len = 0;
  968. for (;;) {
  969. c = DgetC(DODOL);
  970. if (c == DEOF)
  971. break;
  972. if ((c &= TRIM) == 0)
  973. continue;
  974. /* \ quotes \ $ ` here */
  975. if (c == '\\') {
  976. c = DgetC(0);
  977. if (!any("$\\`", c))
  978. unDgetC(c | QUOTE), c = '\\';
  979. else
  980. c |= QUOTE;
  981. }
  982. Strbuf_append1(&mbuf, (Char) c);
  983. }
  984. Strbuf_terminate(&mbuf);
  985. /*
  986. * If any ` in line do command substitution
  987. */
  988. mbp = mbuf.s;
  989. if (Strchr(mbp, '`') != NULL) {
  990. /*
  991. * 1 arg to dobackp causes substitution to be literal. Words are
  992. * broken only at newlines so that all blanks and tabs are
  993. * preserved. Blank lines (null words) are not discarded.
  994. */
  995. words = dobackp(mbp, 1);
  996. }
  997. else
  998. /* Setup trivial vector similar to return of dobackp */
  999. Dv[0] = mbp, Dv[1] = NULL, words = Dv;
  1000. /*
  1001. * Resurrect the words from the command substitution each separated by
  1002. * a newline. Note that the last newline of a command substitution
  1003. * will have been discarded, but we put a newline after the last word
  1004. * because this represents the newline after the last input line!
  1005. */
  1006. for (vp= words; *vp; vp++) {
  1007. for (mbp = *vp; *mbp; mbp++) {
  1008. *obp++ = *mbp & TRIM;
  1009. if (obp == OBUF_END) {
  1010. tmp = short2str(obuf);
  1011. (void) xwrite(0, tmp, strlen (tmp));
  1012. obp = obuf;
  1013. }
  1014. }
  1015. *obp++ = '\n';
  1016. if (obp == OBUF_END) {
  1017. tmp = short2str(obuf);
  1018. (void) xwrite(0, tmp, strlen (tmp));
  1019. obp = obuf;
  1020. }
  1021. }
  1022. if (words != Dv)
  1023. blkfree(words);
  1024. }
  1025. *obp = 0;
  1026. tmp = short2str(obuf);
  1027. (void) xwrite(0, tmp, strlen (tmp));
  1028. (void) lseek(0, (off_t) 0, L_SET);
  1029. cleanup_until(&inheredoc);
  1030. }