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