GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: lex.c,v 1.71 2017/07/04 11:46:15 anton Exp $ */ |
||
2 |
|||
3 |
/* |
||
4 |
* lexical analysis and source input |
||
5 |
*/ |
||
6 |
|||
7 |
#include <ctype.h> |
||
8 |
#include <errno.h> |
||
9 |
#include <libgen.h> |
||
10 |
#include <stdio.h> |
||
11 |
#include <string.h> |
||
12 |
#include <unistd.h> |
||
13 |
|||
14 |
#include "sh.h" |
||
15 |
|||
16 |
/* |
||
17 |
* states while lexing word |
||
18 |
*/ |
||
19 |
#define SINVALID -1 /* invalid state */ |
||
20 |
#define SBASE 0 /* outside any lexical constructs */ |
||
21 |
#define SWORD 1 /* implicit quoting for substitute() */ |
||
22 |
#define SLETPAREN 2 /* inside (( )), implicit quoting */ |
||
23 |
#define SSQUOTE 3 /* inside '' */ |
||
24 |
#define SDQUOTE 4 /* inside "" */ |
||
25 |
#define SBRACE 5 /* inside ${} */ |
||
26 |
#define SCSPAREN 6 /* inside $() */ |
||
27 |
#define SBQUOTE 7 /* inside `` */ |
||
28 |
#define SASPAREN 8 /* inside $(( )) */ |
||
29 |
#define SHEREDELIM 9 /* parsing <<,<<- delimiter */ |
||
30 |
#define SHEREDQUOTE 10 /* parsing " in <<,<<- delimiter */ |
||
31 |
#define SPATTERN 11 /* parsing *(...|...) pattern (*+?@!) */ |
||
32 |
#define STBRACE 12 /* parsing ${..[#%]..} */ |
||
33 |
#define SBRACEQ 13 /* inside "${}" */ |
||
34 |
|||
35 |
/* Structure to keep track of the lexing state and the various pieces of info |
||
36 |
* needed for each particular state. |
||
37 |
*/ |
||
38 |
typedef struct lex_state Lex_state; |
||
39 |
struct lex_state { |
||
40 |
int ls_state; |
||
41 |
union { |
||
42 |
/* $(...) */ |
||
43 |
struct scsparen_info { |
||
44 |
int nparen; /* count open parenthesis */ |
||
45 |
int csstate; /* XXX remove */ |
||
46 |
#define ls_scsparen ls_info.u_scsparen |
||
47 |
} u_scsparen; |
||
48 |
|||
49 |
/* $((...)) */ |
||
50 |
struct sasparen_info { |
||
51 |
int nparen; /* count open parenthesis */ |
||
52 |
int start; /* marks start of $(( in output str */ |
||
53 |
#define ls_sasparen ls_info.u_sasparen |
||
54 |
} u_sasparen; |
||
55 |
|||
56 |
/* ((...)) */ |
||
57 |
struct sletparen_info { |
||
58 |
int nparen; /* count open parenthesis */ |
||
59 |
#define ls_sletparen ls_info.u_sletparen |
||
60 |
} u_sletparen; |
||
61 |
|||
62 |
/* `...` */ |
||
63 |
struct sbquote_info { |
||
64 |
int indquotes; /* true if in double quotes: "`...`" */ |
||
65 |
#define ls_sbquote ls_info.u_sbquote |
||
66 |
} u_sbquote; |
||
67 |
|||
68 |
Lex_state *base; /* used to point to next state block */ |
||
69 |
} ls_info; |
||
70 |
}; |
||
71 |
|||
72 |
typedef struct State_info State_info; |
||
73 |
struct State_info { |
||
74 |
Lex_state *base; |
||
75 |
Lex_state *end; |
||
76 |
}; |
||
77 |
|||
78 |
|||
79 |
static void readhere(struct ioword *); |
||
80 |
static int getsc__(void); |
||
81 |
static void getsc_line(Source *); |
||
82 |
static int getsc_bn(void); |
||
83 |
static char *get_brace_var(XString *, char *); |
||
84 |
static int arraysub(char **); |
||
85 |
static const char *ungetsc(int); |
||
86 |
static void gethere(void); |
||
87 |
static Lex_state *push_state_(State_info *, Lex_state *); |
||
88 |
static Lex_state *pop_state_(State_info *, Lex_state *); |
||
89 |
static char *special_prompt_expand(char *); |
||
90 |
static int dopprompt(const char *, int, const char **, int); |
||
91 |
int promptlen(const char *cp, const char **spp); |
||
92 |
|||
93 |
static int backslash_skip; |
||
94 |
static int ignore_backslash_newline; |
||
95 |
|||
96 |
Source *source; /* yyparse/yylex source */ |
||
97 |
YYSTYPE yylval; /* result from yylex */ |
||
98 |
struct ioword *heres[HERES], **herep; |
||
99 |
char ident[IDENT+1]; |
||
100 |
|||
101 |
char **history; /* saved commands */ |
||
102 |
char **histptr; /* last history item */ |
||
103 |
int histsize; /* history size */ |
||
104 |
|||
105 |
/* optimized getsc_bn() */ |
||
106 |
#define getsc() (*source->str != '\0' && *source->str != '\\' \ |
||
107 |
&& !backslash_skip ? *source->str++ : getsc_bn()) |
||
108 |
/* optimized getsc__() */ |
||
109 |
#define getsc_() ((*source->str != '\0') ? *source->str++ : getsc__()) |
||
110 |
|||
111 |
#define STATE_BSIZE 32 |
||
112 |
|||
113 |
#define PUSH_STATE(s) do { \ |
||
114 |
if (++statep == state_info.end) \ |
||
115 |
statep = push_state_(&state_info, statep); \ |
||
116 |
state = statep->ls_state = (s); \ |
||
117 |
} while (0) |
||
118 |
|||
119 |
#define POP_STATE() do { \ |
||
120 |
if (--statep == state_info.base) \ |
||
121 |
statep = pop_state_(&state_info, statep); \ |
||
122 |
state = statep->ls_state; \ |
||
123 |
} while (0) |
||
124 |
|||
125 |
|||
126 |
|||
127 |
/* |
||
128 |
* Lexical analyzer |
||
129 |
* |
||
130 |
* tokens are not regular expressions, they are LL(1). |
||
131 |
* for example, "${var:-${PWD}}", and "$(size $(whence ksh))". |
||
132 |
* hence the state stack. |
||
133 |
*/ |
||
134 |
|||
135 |
int |
||
136 |
yylex(int cf) |
||
137 |
{ |
||
138 |
170685044 |
Lex_state states[STATE_BSIZE], *statep; |
|
139 |
85342522 |
State_info state_info; |
|
140 |
int c, state; |
||
141 |
85342522 |
XString ws; /* expandable output word */ |
|
142 |
char *wp; /* output word pointer */ |
||
143 |
char *sp, *dp; |
||
144 |
85342522 |
int c2; |
|
145 |
|||
146 |
|||
147 |
Again: |
||
148 |
86975781 |
states[0].ls_state = SINVALID; |
|
149 |
86975781 |
states[0].ls_info.base = NULL; |
|
150 |
86975781 |
statep = &states[1]; |
|
151 |
86975781 |
state_info.base = states; |
|
152 |
86975781 |
state_info.end = &states[STATE_BSIZE]; |
|
153 |
|||
154 |
86975781 |
Xinit(ws, wp, 64, ATEMP); |
|
155 |
|||
156 |
86975781 |
backslash_skip = 0; |
|
157 |
86975781 |
ignore_backslash_newline = 0; |
|
158 |
|||
159 |
✓✓ | 86975781 |
if (cf&ONEWORD) |
160 |
701753 |
state = SWORD; |
|
161 |
✓✓ | 86274028 |
else if (cf&LETEXPR) { |
162 |
50 |
*wp++ = OQUOTE; /* enclose arguments in (double) quotes */ |
|
163 |
state = SLETPAREN; |
||
164 |
50 |
statep->ls_sletparen.nparen = 0; |
|
165 |
50 |
} else { /* normal lexing */ |
|
166 |
86273978 |
state = (cf & HEREDELIM) ? SHEREDELIM : SBASE; |
|
167 |
✓✓✓✓ ✓✓ |
682377313 |
while ((c = getsc()) == ' ' || c == '\t') |
168 |
; |
||
169 |
✓✓ | 86271116 |
if (c == '#') { |
170 |
2276800 |
ignore_backslash_newline++; |
|
171 |
✓✗✓✓ ✓✓ |
412452008 |
while ((c = getsc()) != '\0' && c != '\n') |
172 |
; |
||
173 |
2276800 |
ignore_backslash_newline--; |
|
174 |
2276800 |
} |
|
175 |
86271116 |
ungetsc(c); |
|
176 |
} |
||
177 |
✓✓ | 86972919 |
if (source->flags & SF_ALIAS) { /* trailing ' ' in alias definition */ |
178 |
546 |
source->flags &= ~SF_ALIAS; |
|
179 |
/* In POSIX mode, a trailing space only counts if we are |
||
180 |
* parsing a simple command |
||
181 |
*/ |
||
182 |
✗✓✗✗ |
546 |
if (!Flag(FPOSIX) || (cf & CMDWORD)) |
183 |
546 |
cf |= ALIAS; |
|
184 |
} |
||
185 |
|||
186 |
/* Initial state: one of SBASE SHEREDELIM SWORD SASPAREN */ |
||
187 |
86972919 |
statep->ls_state = state; |
|
188 |
|||
189 |
/* collect non-special or quoted characters to form word */ |
||
190 |
✓✓✓✓ ✓✓✓✓ |
2622347664 |
while (!((c = getsc()) == 0 || |
191 |
✓✓ | 812458312 |
((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) { |
192 |
✓✓ | 424487368 |
Xcheck(ws, wp); |
193 |
✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓ |
583163182 |
switch (state) { |
194 |
case SBASE: |
||
195 |
✗✓✗✗ |
231104350 |
if (Flag(FCSHHISTORY) && (source->flags & SF_TTY) && |
196 |
c == '!') { |
||
197 |
char **replace = NULL; |
||
198 |
int get, i; |
||
199 |
char match[200] = { 0 }, *str = match; |
||
200 |
size_t mlen; |
||
201 |
|||
202 |
c2 = getsc(); |
||
203 |
if (c2 == '\0' || c2 == ' ' || c2 == '\t') |
||
204 |
; |
||
205 |
else if (c2 == '!') |
||
206 |
replace = hist_get_newest(0); |
||
207 |
else if (isdigit(c2) || c2 == '-' || |
||
208 |
isalpha(c2)) { |
||
209 |
get = !isalpha(c2); |
||
210 |
|||
211 |
*str++ = c2; |
||
212 |
do { |
||
213 |
if ((c2 = getsc()) == '\0') |
||
214 |
break; |
||
215 |
if (c2 == '\t' || c2 == ' ' || |
||
216 |
c2 == '\n') { |
||
217 |
ungetsc(c2); |
||
218 |
break; |
||
219 |
} |
||
220 |
*str++ = c2; |
||
221 |
} while (str < &match[sizeof(match)-1]); |
||
222 |
*str = '\0'; |
||
223 |
|||
224 |
if (get) { |
||
225 |
int h = findhistrel(match); |
||
226 |
if (h >= 0) |
||
227 |
replace = &history[h]; |
||
228 |
} else { |
||
229 |
int h = findhist(-1, 0, match, true); |
||
230 |
if (h >= 0) |
||
231 |
replace = &history[h]; |
||
232 |
} |
||
233 |
} |
||
234 |
|||
235 |
/* |
||
236 |
* XXX ksh history buffer saves un-expanded |
||
237 |
* commands. Until the history buffer code is |
||
238 |
* changed to contain expanded commands, we |
||
239 |
* ignore the bad commands (spinning sucks) |
||
240 |
*/ |
||
241 |
if (replace && **replace == '!') |
||
242 |
ungetsc(c2); |
||
243 |
else if (replace) { |
||
244 |
Source *s; |
||
245 |
|||
246 |
/* do not strdup replacement via alloc */ |
||
247 |
s = pushs(SREREAD, source->areap); |
||
248 |
s->start = s->str = *replace; |
||
249 |
s->next = source; |
||
250 |
s->u.freeme = NULL; |
||
251 |
source = s; |
||
252 |
continue; |
||
253 |
} else if (*match != '\0') { |
||
254 |
/* restore what followed the '!' */ |
||
255 |
mlen = strlen(match); |
||
256 |
for (i = mlen-1; i >= 0; i--) |
||
257 |
ungetsc(match[i]); |
||
258 |
} else |
||
259 |
ungetsc(c2); |
||
260 |
} |
||
261 |
✓✓✓✓ |
231330955 |
if (c == '[' && (cf & (VARASN|ARRAYVAR))) { |
262 |
167375 |
*wp = EOS; /* temporary */ |
|
263 |
✓✓ | 167375 |
if (is_wdvarname(Xstring(ws, wp), false)) { |
264 |
823 |
char *p, *tmp; |
|
265 |
|||
266 |
✓✗ | 823 |
if (arraysub(&tmp)) { |
267 |
823 |
*wp++ = CHAR; |
|
268 |
823 |
*wp++ = c; |
|
269 |
✓✓ | 13588 |
for (p = tmp; *p; ) { |
270 |
✗✓ | 5971 |
Xcheck(ws, wp); |
271 |
5971 |
*wp++ = CHAR; |
|
272 |
5971 |
*wp++ = *p++; |
|
273 |
} |
||
274 |
823 |
afree(tmp, ATEMP); |
|
275 |
823 |
break; |
|
276 |
} else { |
||
277 |
Source *s; |
||
278 |
|||
279 |
s = pushs(SREREAD, |
||
280 |
source->areap); |
||
281 |
s->start = s->str |
||
282 |
= s->u.freeme = tmp; |
||
283 |
s->next = source; |
||
284 |
source = s; |
||
285 |
} |
||
286 |
✓✗ | 823 |
} |
287 |
166552 |
*wp++ = CHAR; |
|
288 |
166552 |
*wp++ = c; |
|
289 |
166552 |
break; |
|
290 |
} |
||
291 |
/* FALLTHROUGH */ |
||
292 |
Sbase1: /* includes *(...|...) pattern (*+?@!) */ |
||
293 |
✓✓ | 470592476 |
if (c == '*' || c == '@' || c == '+' || c == '?' || |
294 |
235296238 |
c == '!') { |
|
295 |
✓✓✓✓ |
10783917 |
c2 = getsc(); |
296 |
✓✓ | 2696775 |
if (c2 == '(' /*)*/ ) { |
297 |
8714 |
*wp++ = OPAT; |
|
298 |
8714 |
*wp++ = c; |
|
299 |
✗✓ | 17428 |
PUSH_STATE(SPATTERN); |
300 |
8714 |
break; |
|
301 |
} |
||
302 |
2688061 |
ungetsc(c2); |
|
303 |
2688061 |
} |
|
304 |
/* FALLTHROUGH */ |
||
305 |
Sbase2: /* doesn't include *(...|...) pattern (*+?@!) */ |
||
306 |
✓✓✓✓ |
237793424 |
switch (c) { |
307 |
case '\\': |
||
308 |
✓✓✗✓ |
2498493 |
c = getsc(); |
309 |
✓✓ | 624642 |
if (c) /* trailing \ is lost */ |
310 |
624567 |
*wp++ = QCHAR, *wp++ = c; |
|
311 |
break; |
||
312 |
case '\'': |
||
313 |
✓✓ | 1512931 |
if ((cf & HEREDOC) || state == SBRACEQ) { |
314 |
4258 |
*wp++ = CHAR, *wp++ = c; |
|
315 |
4258 |
break; |
|
316 |
} |
||
317 |
1508673 |
*wp++ = OQUOTE; |
|
318 |
1508673 |
ignore_backslash_newline++; |
|
319 |
✗✓ | 3017346 |
PUSH_STATE(SSQUOTE); |
320 |
1508673 |
break; |
|
321 |
case '"': |
||
322 |
8449707 |
*wp++ = OQUOTE; |
|
323 |
✗✓ | 16899414 |
PUSH_STATE(SDQUOTE); |
324 |
8449707 |
break; |
|
325 |
default: |
||
326 |
goto Subst; |
||
327 |
} |
||
328 |
break; |
||
329 |
|||
330 |
Subst: |
||
331 |
✓✓✓✓ |
335261021 |
switch (c) { |
332 |
case '\\': |
||
333 |
✓✗✗✓ |
14598312 |
c = getsc(); |
334 |
✗✗✓✓ ✓ |
3649578 |
switch (c) { |
335 |
case '\\': |
||
336 |
case '$': case '`': |
||
337 |
347927 |
*wp++ = QCHAR, *wp++ = c; |
|
338 |
347927 |
break; |
|
339 |
case '"': |
||
340 |
✓✗ | 374041 |
if ((cf & HEREDOC) == 0) { |
341 |
374041 |
*wp++ = QCHAR, *wp++ = c; |
|
342 |
374041 |
break; |
|
343 |
} |
||
344 |
/* FALLTHROUGH */ |
||
345 |
default: |
||
346 |
✗✓ | 2927610 |
if (cf & UNESCAPE) { |
347 |
*wp++ = QCHAR, *wp++ = c; |
||
348 |
break; |
||
349 |
} |
||
350 |
✗✓ | 2927610 |
Xcheck(ws, wp); |
351 |
✗✓ | 2927610 |
if (c) { /* trailing \ is lost */ |
352 |
2927610 |
*wp++ = CHAR, *wp++ = '\\'; |
|
353 |
2927610 |
*wp++ = CHAR, *wp++ = c; |
|
354 |
2927610 |
} |
|
355 |
break; |
||
356 |
} |
||
357 |
break; |
||
358 |
case '$': |
||
359 |
✓✗✓✓ |
60637556 |
c = getsc(); |
360 |
✓✓ | 15159389 |
if (c == '(') /*)*/ { |
361 |
✓✗✓✓ |
182604 |
c = getsc(); |
362 |
✓✓ | 45651 |
if (c == '(') /*)*/ { |
363 |
✗✓ | 28712 |
PUSH_STATE(SASPAREN); |
364 |
14356 |
statep->ls_sasparen.nparen = 2; |
|
365 |
14356 |
statep->ls_sasparen.start = |
|
366 |
14356 |
Xsavepos(ws, wp); |
|
367 |
14356 |
*wp++ = EXPRSUB; |
|
368 |
14356 |
} else { |
|
369 |
31295 |
ungetsc(c); |
|
370 |
✗✓ | 62590 |
PUSH_STATE(SCSPAREN); |
371 |
31295 |
statep->ls_scsparen.nparen = 1; |
|
372 |
31295 |
statep->ls_scsparen.csstate = 0; |
|
373 |
31295 |
*wp++ = COMSUB; |
|
374 |
} |
||
375 |
✓✓ | 15113738 |
} else if (c == '{') /*}*/ { |
376 |
2538935 |
*wp++ = OSUBST; |
|
377 |
2538935 |
*wp++ = '{'; /*}*/ |
|
378 |
2538935 |
wp = get_brace_var(&ws, wp); |
|
379 |
✓✗✓✗ |
10155740 |
c = getsc(); |
380 |
/* allow :# and :% (ksh88 compat) */ |
||
381 |
✓✓ | 2538935 |
if (c == ':') { |
382 |
178775 |
*wp++ = CHAR, *wp++ = c; |
|
383 |
✓✗✓✗ |
715100 |
c = getsc(); |
384 |
178775 |
} |
|
385 |
/* If this is a trim operation, |
||
386 |
* treat (,|,) specially in STBRACE. |
||
387 |
*/ |
||
388 |
✓✓ | 5077870 |
if (c == '#' || c == '%') { |
389 |
2538935 |
ungetsc(c); |
|
390 |
✗✓ | 15870 |
PUSH_STATE(STBRACE); |
391 |
7935 |
} else { |
|
392 |
ungetsc(c); |
||
393 |
✓✓ | 5062000 |
if (state == SDQUOTE || |
394 |
2531000 |
state == SBRACEQ) |
|
395 |
✗✓ | 2567774 |
PUSH_STATE(SBRACEQ); |
396 |
else |
||
397 |
✗✓ | 2494226 |
PUSH_STATE(SBRACE); |
398 |
} |
||
399 |
✓✓ | 12574803 |
} else if (ctype(c, C_ALPHA)) { |
400 |
10942395 |
*wp++ = OSUBST; |
|
401 |
10942395 |
*wp++ = 'X'; |
|
402 |
10942395 |
do { |
|
403 |
✓✓ | 73890142 |
Xcheck(ws, wp); |
404 |
73685645 |
*wp++ = c; |
|
405 |
✓✓✓✓ |
294405857 |
c = getsc(); |
406 |
✓✓✓✓ |
85005146 |
} while (ctype(c, C_ALPHA) || digit(c)); |
407 |
10942395 |
*wp++ = '\0'; |
|
408 |
10942395 |
*wp++ = CSUBST; |
|
409 |
10942395 |
*wp++ = 'X'; |
|
410 |
10942395 |
ungetsc(c); |
|
411 |
✓✓✓✓ |
13978943 |
} else if (ctype(c, C_VAR1) || digit(c)) { |
412 |
✗✓ | 1622082 |
Xcheck(ws, wp); |
413 |
1622082 |
*wp++ = OSUBST; |
|
414 |
1622082 |
*wp++ = 'X'; |
|
415 |
1622082 |
*wp++ = c; |
|
416 |
1622082 |
*wp++ = '\0'; |
|
417 |
1622082 |
*wp++ = CSUBST; |
|
418 |
1622082 |
*wp++ = 'X'; |
|
419 |
1622082 |
} else { |
|
420 |
10326 |
*wp++ = CHAR, *wp++ = '$'; |
|
421 |
10326 |
ungetsc(c); |
|
422 |
} |
||
423 |
break; |
||
424 |
case '`': |
||
425 |
✗✓ | 647240 |
PUSH_STATE(SBQUOTE); |
426 |
323620 |
*wp++ = COMSUB; |
|
427 |
/* Need to know if we are inside double quotes |
||
428 |
* since sh/at&t-ksh translate the \" to " in |
||
429 |
* "`..\"..`". |
||
430 |
*/ |
||
431 |
323620 |
statep->ls_sbquote.indquotes = 0; |
|
432 |
Lex_state *s = statep; |
||
433 |
323620 |
Lex_state *base = state_info.base; |
|
434 |
323620 |
while (1) { |
|
435 |
✓✓ | 1864156 |
for (; s != base; s--) { |
436 |
✓✓ | 647490 |
if (s->ls_state == SDQUOTE) { |
437 |
39032 |
statep->ls_sbquote.indquotes = 1; |
|
438 |
39032 |
break; |
|
439 |
} |
||
440 |
} |
||
441 |
✓✓ | 323620 |
if (s != base) |
442 |
break; |
||
443 |
✓✗ | 284588 |
if (!(s = s->ls_info.base)) |
444 |
break; |
||
445 |
base = s-- - STATE_BSIZE; |
||
446 |
} |
||
447 |
break; |
||
448 |
default: |
||
449 |
316128434 |
*wp++ = CHAR, *wp++ = c; |
|
450 |
316128434 |
} |
|
451 |
break; |
||
452 |
|||
453 |
case SSQUOTE: |
||
454 |
✓✓ | 48392783 |
if (c == '\'') { |
455 |
✗✓ | 3017794 |
POP_STATE(); |
456 |
✗✓ | 1508897 |
if (state == SBRACEQ) { |
457 |
*wp++ = CHAR, *wp++ = c; |
||
458 |
break; |
||
459 |
} |
||
460 |
1508897 |
*wp++ = CQUOTE; |
|
461 |
1508897 |
ignore_backslash_newline--; |
|
462 |
1508897 |
} else |
|
463 |
46883886 |
*wp++ = QCHAR, *wp++ = c; |
|
464 |
break; |
||
465 |
|||
466 |
case SDQUOTE: |
||
467 |
✓✓ | 82107280 |
if (c == '"') { |
468 |
✗✓ | 16899414 |
POP_STATE(); |
469 |
8449707 |
*wp++ = CQUOTE; |
|
470 |
} else |
||
471 |
goto Subst; |
||
472 |
8449707 |
break; |
|
473 |
|||
474 |
case SCSPAREN: /* $( .. ) */ |
||
475 |
/* todo: deal with $(...) quoting properly |
||
476 |
* kludge to partly fake quoting inside $(..): doesn't |
||
477 |
* really work because nested $(..) or ${..} inside |
||
478 |
* double quotes aren't dealt with. |
||
479 |
*/ |
||
480 |
✓✗✓✓ ✓✓ |
1032681 |
switch (statep->ls_scsparen.csstate) { |
481 |
case 0: /* normal */ |
||
482 |
✗✓✓✓ ✓✓ |
788856 |
switch (c) { |
483 |
case '(': |
||
484 |
statep->ls_scsparen.nparen++; |
||
485 |
break; |
||
486 |
case ')': |
||
487 |
31295 |
statep->ls_scsparen.nparen--; |
|
488 |
31295 |
break; |
|
489 |
case '\\': |
||
490 |
8204 |
statep->ls_scsparen.csstate = 1; |
|
491 |
8204 |
break; |
|
492 |
case '"': |
||
493 |
23874 |
statep->ls_scsparen.csstate = 2; |
|
494 |
23874 |
break; |
|
495 |
case '\'': |
||
496 |
1942 |
statep->ls_scsparen.csstate = 4; |
|
497 |
1942 |
ignore_backslash_newline++; |
|
498 |
1942 |
break; |
|
499 |
} |
||
500 |
break; |
||
501 |
|||
502 |
case 1: /* backslash in normal mode */ |
||
503 |
case 3: /* backslash in double quotes */ |
||
504 |
8658 |
--statep->ls_scsparen.csstate; |
|
505 |
8658 |
break; |
|
506 |
|||
507 |
case 2: /* double quotes */ |
||
508 |
✓✓ | 127858 |
if (c == '"') |
509 |
23874 |
statep->ls_scsparen.csstate = 0; |
|
510 |
✓✓ | 103984 |
else if (c == '\\') |
511 |
454 |
statep->ls_scsparen.csstate = 3; |
|
512 |
break; |
||
513 |
|||
514 |
case 4: /* single quotes */ |
||
515 |
✓✓ | 18054 |
if (c == '\'') { |
516 |
1942 |
statep->ls_scsparen.csstate = 0; |
|
517 |
1942 |
ignore_backslash_newline--; |
|
518 |
1942 |
} |
|
519 |
break; |
||
520 |
} |
||
521 |
✓✓ | 878111 |
if (statep->ls_scsparen.nparen == 0) { |
522 |
✗✓ | 62590 |
POP_STATE(); |
523 |
31295 |
*wp++ = 0; /* end of COMSUB */ |
|
524 |
31295 |
} else |
|
525 |
846816 |
*wp++ = c; |
|
526 |
break; |
||
527 |
|||
528 |
case SASPAREN: /* $(( .. )) */ |
||
529 |
/* todo: deal with $((...); (...)) properly */ |
||
530 |
/* XXX should nest using existing state machine |
||
531 |
* (embed "..", $(...), etc.) */ |
||
532 |
✓✓ | 138562 |
if (c == '(') |
533 |
100 |
statep->ls_sasparen.nparen++; |
|
534 |
✓✓ | 138462 |
else if (c == ')') { |
535 |
14456 |
statep->ls_sasparen.nparen--; |
|
536 |
✓✓ | 14456 |
if (statep->ls_sasparen.nparen == 1) { |
537 |
/*(*/ |
||
538 |
✓✗✓✓ ✓✗ |
57424 |
if ((c2 = getsc()) == ')') { |
539 |
✗✓ | 28712 |
POP_STATE(); |
540 |
14356 |
*wp++ = 0; /* end of EXPRSUB */ |
|
541 |
14356 |
break; |
|
542 |
} else { |
||
543 |
char *s; |
||
544 |
|||
545 |
ungetsc(c2); |
||
546 |
/* mismatched parenthesis - |
||
547 |
* assume we were really |
||
548 |
* parsing a $(..) expression |
||
549 |
*/ |
||
550 |
s = Xrestpos(ws, wp, |
||
551 |
statep->ls_sasparen.start); |
||
552 |
memmove(s + 1, s, wp - s); |
||
553 |
*s++ = COMSUB; |
||
554 |
*s = '('; /*)*/ |
||
555 |
wp++; |
||
556 |
statep->ls_scsparen.nparen = 1; |
||
557 |
statep->ls_scsparen.csstate = 0; |
||
558 |
state = statep->ls_state = |
||
559 |
SCSPAREN; |
||
560 |
} |
||
561 |
} |
||
562 |
} |
||
563 |
124206 |
*wp++ = c; |
|
564 |
124206 |
break; |
|
565 |
|||
566 |
case SBRACEQ: |
||
567 |
/*{*/ |
||
568 |
✓✓ | 3789587 |
if (c == '}') { |
569 |
✗✓ | 2567774 |
POP_STATE(); |
570 |
1283887 |
*wp++ = CSUBST; |
|
571 |
1283887 |
*wp++ = /*{*/ '}'; |
|
572 |
} else |
||
573 |
goto Sbase2; |
||
574 |
1283887 |
break; |
|
575 |
|||
576 |
case SBRACE: |
||
577 |
/*{*/ |
||
578 |
✓✓ | 5455190 |
if (c == '}') { |
579 |
✗✓ | 2494226 |
POP_STATE(); |
580 |
1247113 |
*wp++ = CSUBST; |
|
581 |
1247113 |
*wp++ = /*{*/ '}'; |
|
582 |
} else |
||
583 |
goto Sbase1; |
||
584 |
1247113 |
break; |
|
585 |
|||
586 |
case STBRACE: |
||
587 |
/* Same as SBRACE, except (,|,) treated specially */ |
||
588 |
/*{*/ |
||
589 |
✓✓ | 32219 |
if (c == '}') { |
590 |
✗✓ | 15870 |
POP_STATE(); |
591 |
7935 |
*wp++ = CSUBST; |
|
592 |
7935 |
*wp++ = /*{*/ '}'; |
|
593 |
✓✓ | 32219 |
} else if (c == '|') { |
594 |
100 |
*wp++ = SPAT; |
|
595 |
✓✓ | 24284 |
} else if (c == '(') { |
596 |
25 |
*wp++ = OPAT; |
|
597 |
25 |
*wp++ = ' '; /* simile for @ */ |
|
598 |
✗✓ | 50 |
PUSH_STATE(SPATTERN); |
599 |
} else |
||
600 |
goto Sbase1; |
||
601 |
break; |
||
602 |
|||
603 |
case SBQUOTE: |
||
604 |
✓✓ | 14345336 |
if (c == '`') { |
605 |
323620 |
*wp++ = 0; |
|
606 |
✗✓ | 647240 |
POP_STATE(); |
607 |
✓✓ | 14345336 |
} else if (c == '\\') { |
608 |
✓✗✗✓ ✗✗✓✓ ✓ |
1172674 |
switch (c = getsc()) { |
609 |
case '\\': |
||
610 |
case '$': case '`': |
||
611 |
34390 |
*wp++ = c; |
|
612 |
34390 |
break; |
|
613 |
case '"': |
||
614 |
✓✓ | 6390 |
if (statep->ls_sbquote.indquotes) { |
615 |
6052 |
*wp++ = c; |
|
616 |
6052 |
break; |
|
617 |
} |
||
618 |
/* FALLTHROUGH */ |
||
619 |
default: |
||
620 |
✗✓ | 252642 |
if (c) { /* trailing \ is lost */ |
621 |
252642 |
*wp++ = '\\'; |
|
622 |
252642 |
*wp++ = c; |
|
623 |
252642 |
} |
|
624 |
break; |
||
625 |
} |
||
626 |
} else |
||
627 |
13728632 |
*wp++ = c; |
|
628 |
break; |
||
629 |
|||
630 |
case SWORD: /* ONEWORD */ |
||
631 |
goto Subst; |
||
632 |
|||
633 |
case SLETPAREN: /* LETEXPR: (( ... )) */ |
||
634 |
/*(*/ |
||
635 |
✓✓ | 250 |
if (c == ')') { |
636 |
✗✓ | 50 |
if (statep->ls_sletparen.nparen > 0) |
637 |
--statep->ls_sletparen.nparen; |
||
638 |
/*(*/ |
||
639 |
✓✗✓✓ ✓✗ |
200 |
else if ((c2 = getsc()) == ')') { |
640 |
c = 0; |
||
641 |
50 |
*wp++ = CQUOTE; |
|
642 |
50 |
goto Done; |
|
643 |
} else |
||
644 |
ungetsc(c2); |
||
645 |
✓✗ | 200 |
} else if (c == '(') |
646 |
/* parenthesis inside quotes and backslashes |
||
647 |
* are lost, but at&t ksh doesn't count them |
||
648 |
* either |
||
649 |
*/ |
||
650 |
++statep->ls_sletparen.nparen; |
||
651 |
goto Sbase2; |
||
652 |
|||
653 |
case SHEREDELIM: /* <<,<<- delimiter */ |
||
654 |
/* XXX chuck this state (and the next) - use |
||
655 |
* the existing states ($ and \`..` should be |
||
656 |
* stripped of their specialness after the |
||
657 |
* fact). |
||
658 |
*/ |
||
659 |
/* here delimiters need a special case since |
||
660 |
* $ and `..` are not to be treated specially |
||
661 |
*/ |
||
662 |
✓✓ | 1353817 |
if (c == '\\') { |
663 |
✓✗✗✓ |
103268 |
c = getsc(); |
664 |
✗✓ | 25817 |
if (c) { /* trailing \ is lost */ |
665 |
25817 |
*wp++ = QCHAR; |
|
666 |
25817 |
*wp++ = c; |
|
667 |
25817 |
} |
|
668 |
✓✓ | 1328000 |
} else if (c == '\'') { |
669 |
✗✓ | 448 |
PUSH_STATE(SSQUOTE); |
670 |
224 |
*wp++ = OQUOTE; |
|
671 |
224 |
ignore_backslash_newline++; |
|
672 |
✓✓ | 1328000 |
} else if (c == '"') { |
673 |
100 |
state = statep->ls_state = SHEREDQUOTE; |
|
674 |
100 |
*wp++ = OQUOTE; |
|
675 |
100 |
} else { |
|
676 |
1327676 |
*wp++ = CHAR; |
|
677 |
1327676 |
*wp++ = c; |
|
678 |
} |
||
679 |
break; |
||
680 |
|||
681 |
case SHEREDQUOTE: /* " in <<,<<- delimiter */ |
||
682 |
✓✓ | 400 |
if (c == '"') { |
683 |
100 |
*wp++ = CQUOTE; |
|
684 |
100 |
state = statep->ls_state = SHEREDELIM; |
|
685 |
100 |
} else { |
|
686 |
✓✓ | 300 |
if (c == '\\') { |
687 |
✓✗✗✓ ✗✗✗✗ ✓ |
100 |
switch (c = getsc()) { |
688 |
case '\\': case '"': |
||
689 |
case '$': case '`': |
||
690 |
break; |
||
691 |
default: |
||
692 |
if (c) { /* trailing \ lost */ |
||
693 |
*wp++ = CHAR; |
||
694 |
*wp++ = '\\'; |
||
695 |
} |
||
696 |
break; |
||
697 |
} |
||
698 |
} |
||
699 |
300 |
*wp++ = CHAR; |
|
700 |
300 |
*wp++ = c; |
|
701 |
} |
||
702 |
break; |
||
703 |
|||
704 |
case SPATTERN: /* in *(...|...) pattern (*+?@!) */ |
||
705 |
✓✓ | 148702 |
if ( /*(*/ c == ')') { |
706 |
8839 |
*wp++ = CPAT; |
|
707 |
✗✓ | 17678 |
POP_STATE(); |
708 |
✓✓ | 148702 |
} else if (c == '|') { |
709 |
12736 |
*wp++ = SPAT; |
|
710 |
✓✓ | 139863 |
} else if (c == '(') { |
711 |
100 |
*wp++ = OPAT; |
|
712 |
100 |
*wp++ = ' '; /* simile for @ */ |
|
713 |
✗✓ | 200 |
PUSH_STATE(SPATTERN); |
714 |
} else |
||
715 |
goto Sbase1; |
||
716 |
break; |
||
717 |
} |
||
718 |
} |
||
719 |
Done: |
||
720 |
✓✓ | 87118807 |
Xcheck(ws, wp); |
721 |
✗✓ | 86972919 |
if (statep != &states[1]) |
722 |
/* XXX figure out what is missing */ |
||
723 |
yyerror("no closing quote\n"); |
||
724 |
|||
725 |
/* This done to avoid tests for SHEREDELIM wherever SBASE tested */ |
||
726 |
✓✓ | 86972919 |
if (state == SHEREDELIM) |
727 |
233676 |
state = SBASE; |
|
728 |
|||
729 |
86972919 |
dp = Xstring(ws, wp); |
|
730 |
✓✓✓✓ |
87609906 |
if ((c == '<' || c == '>') && state == SBASE && |
731 |
✓✓ | 2847787 |
((c2 = Xlength(ws, wp)) == 0 || |
732 |
✓✓✓✗ |
1911026 |
(c2 == 2 && dp[0] == CHAR && digit(dp[1])))) { |
733 |
2847704 |
struct ioword *iop = alloc(sizeof(*iop), ATEMP); |
|
734 |
|||
735 |
✓✓ | 2847704 |
if (c2 == 2) |
736 |
636969 |
iop->unit = dp[1] - '0'; |
|
737 |
else |
||
738 |
2210735 |
iop->unit = c == '>'; /* 0 for <, 1 for > */ |
|
739 |
|||
740 |
✓✗✓✓ |
11390816 |
c2 = getsc(); |
741 |
/* <<, >>, <> are ok, >< is not */ |
||
742 |
✓✓✗✓ |
5262054 |
if (c == c2 || (c == '<' && c2 == '>')) { |
743 |
✓✗ | 1300062 |
iop->flag = c == c2 ? |
744 |
433354 |
(c == '>' ? IOCAT : IOHERE) : IORDWR; |
|
745 |
✓✓ | 433354 |
if (iop->flag == IOHERE) { |
746 |
✓✗✓✓ ✓✓ |
934704 |
if ((c2 = getsc()) == '-') |
747 |
450 |
iop->flag |= IOSKIP; |
|
748 |
else |
||
749 |
233226 |
ungetsc(c2); |
|
750 |
} |
||
751 |
✓✓ | 2414350 |
} else if (c2 == '&') |
752 |
1781367 |
iop->flag = IODUP | (c == '<' ? IORDUP : 0); |
|
753 |
else { |
||
754 |
632983 |
iop->flag = c == '>' ? IOWRITE : IOREAD; |
|
755 |
✗✓ | 632983 |
if (c == '>' && c2 == '|') |
756 |
iop->flag |= IOCLOB; |
||
757 |
else |
||
758 |
632983 |
ungetsc(c2); |
|
759 |
} |
||
760 |
|||
761 |
2847704 |
iop->name = NULL; |
|
762 |
2847704 |
iop->delim = NULL; |
|
763 |
2847704 |
iop->heredoc = NULL; |
|
764 |
2847704 |
Xfree(ws, wp); /* free word */ |
|
765 |
2847704 |
yylval.iop = iop; |
|
766 |
return REDIR; |
||
767 |
} |
||
768 |
|||
769 |
✓✓ | 84125215 |
if (wp == dp && state == SBASE) { |
770 |
31095429 |
Xfree(ws, wp); /* free word */ |
|
771 |
/* no word, process LEX1 character */ |
||
772 |
✓✗✗✓ ✓✓✓ |
31095429 |
switch (c) { |
773 |
default: |
||
774 |
4191224 |
return c; |
|
775 |
|||
776 |
case '|': |
||
777 |
case '&': |
||
778 |
case ';': |
||
779 |
✓✓✓✓ ✓✓ |
35267374 |
if ((c2 = getsc()) == c) |
780 |
✓✓ | 3209096 |
c = (c == ';') ? BREAK : |
781 |
✓✓ | 1277966 |
(c == '|') ? LOGOR : |
782 |
531832 |
(c == '&') ? LOGAND : |
|
783 |
YYERRCODE; |
||
784 |
✗✓ | 7212398 |
else if (c == '|' && c2 == '&') |
785 |
c = COPROC; |
||
786 |
else |
||
787 |
7212398 |
ungetsc(c2); |
|
788 |
8816946 |
return c; |
|
789 |
|||
790 |
case '\n': |
||
791 |
16275837 |
gethere(); |
|
792 |
✓✓ | 16275837 |
if (cf & CONTIN) |
793 |
goto Again; |
||
794 |
14657626 |
return c; |
|
795 |
|||
796 |
case '(': /*)*/ |
||
797 |
✓✓ | 474712 |
if (!Flag(FSH)) { |
798 |
✓✗✓✓ ✓✓ |
84504 |
if ((c2 = getsc()) == '(') /*)*/ |
799 |
/* XXX need to handle ((...); (...)) */ |
||
800 |
50 |
c = MDPAREN; |
|
801 |
else |
||
802 |
21076 |
ungetsc(c2); |
|
803 |
} |
||
804 |
474712 |
return c; |
|
805 |
/*(*/ |
||
806 |
case ')': |
||
807 |
1336685 |
return c; |
|
808 |
} |
||
809 |
} |
||
810 |
|||
811 |
53029786 |
*wp++ = EOS; /* terminate word */ |
|
812 |
53029786 |
yylval.cp = Xclose(ws, wp); |
|
813 |
✓✓ | 53029786 |
if (state == SWORD || state == SLETPAREN) /* ONEWORD? */ |
814 |
701803 |
return LWORD; |
|
815 |
52327983 |
ungetsc(c); /* unget terminator */ |
|
816 |
|||
817 |
/* copy word to unprefixed string ident */ |
||
818 |
✓✓✓✓ |
792750231 |
for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; ) |
819 |
211922382 |
*dp++ = *sp++; |
|
820 |
/* Make sure the ident array stays '\0' padded */ |
||
821 |
52327983 |
memset(dp, 0, (ident+IDENT) - dp + 1); |
|
822 |
✓✓ | 52327983 |
if (c != EOS) |
823 |
15908105 |
*ident = '\0'; /* word is not unquoted */ |
|
824 |
|||
825 |
✓✓✓✓ |
88747861 |
if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) { |
826 |
struct tbl *p; |
||
827 |
20440365 |
int h = hash(ident); |
|
828 |
|||
829 |
/* { */ |
||
830 |
✓✓✓✓ ✗✗ |
40700413 |
if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) && |
831 |
✓✓✗✓ |
6427711 |
(!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}')) { |
832 |
6162065 |
afree(yylval.cp, ATEMP); |
|
833 |
6162065 |
return p->val.i; |
|
834 |
} |
||
835 |
✓✓✓✓ ✓✗ |
27852159 |
if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) && |
836 |
15344 |
(p->flag & ISSET)) { |
|
837 |
Source *s; |
||
838 |
|||
839 |
✓✓ | 31188 |
for (s = source; s->type == SALIAS; s = s->next) |
840 |
✓✓ | 546 |
if (s->u.tblp == p) |
841 |
296 |
return LWORD; |
|
842 |
/* push alias expansion */ |
||
843 |
15048 |
s = pushs(SALIAS, source->areap); |
|
844 |
15048 |
s->start = s->str = p->val.s; |
|
845 |
15048 |
s->u.tblp = p; |
|
846 |
15048 |
s->next = source; |
|
847 |
15048 |
source = s; |
|
848 |
15048 |
afree(yylval.cp, ATEMP); |
|
849 |
15048 |
goto Again; |
|
850 |
} |
||
851 |
✓✓✓ | 14262956 |
} |
852 |
|||
853 |
46150574 |
return LWORD; |
|
854 |
85339635 |
} |
|
855 |
|||
856 |
static void |
||
857 |
gethere(void) |
||
858 |
{ |
||
859 |
struct ioword **p; |
||
860 |
|||
861 |
✓✓ | 49294913 |
for (p = heres; p < herep; p++) |
862 |
233676 |
readhere(*p); |
|
863 |
16275837 |
herep = heres; |
|
864 |
16275837 |
} |
|
865 |
|||
866 |
/* |
||
867 |
* read "<<word" text into temp file |
||
868 |
*/ |
||
869 |
|||
870 |
static void |
||
871 |
readhere(struct ioword *iop) |
||
872 |
{ |
||
873 |
int c; |
||
874 |
467352 |
char *volatile eof; |
|
875 |
char *eofp; |
||
876 |
int skiptabs; |
||
877 |
233676 |
XString xs; |
|
878 |
char *xp; |
||
879 |
int xpos; |
||
880 |
|||
881 |
233676 |
eof = evalstr(iop->delim, 0); |
|
882 |
|||
883 |
✓✓ | 233676 |
if (!(iop->flag & IOEVAL)) |
884 |
26141 |
ignore_backslash_newline++; |
|
885 |
|||
886 |
233676 |
Xinit(xs, xp, 256, ATEMP); |
|
887 |
|||
888 |
3046657 |
for (;;) { |
|
889 |
3046657 |
eofp = eof; |
|
890 |
3046657 |
skiptabs = iop->flag & IOSKIP; |
|
891 |
3046657 |
xpos = Xsavepos(xs, xp); |
|
892 |
✓✓✓✓ ✓✓ |
18969156 |
while ((c = getsc()) != 0) { |
893 |
✓✓ | 4403066 |
if (skiptabs) { |
894 |
✓✓ | 3075 |
if (c == '\t') |
895 |
continue; |
||
896 |
skiptabs = 0; |
||
897 |
1250 |
} |
|
898 |
✓✓ | 4401241 |
if (c != *eofp) |
899 |
break; |
||
900 |
✓✓ | 1357389 |
Xcheck(xs, xp); |
901 |
1354643 |
Xput(xs, xp, c); |
|
902 |
1354643 |
eofp++; |
|
903 |
} |
||
904 |
/* Allow EOF here so commands with out trailing newlines |
||
905 |
* will work (eg, ksh -c '...', $(...), etc). |
||
906 |
*/ |
||
907 |
✓✓✓✗ |
3280308 |
if (*eofp == '\0' && (c == 0 || c == '\n')) { |
908 |
233651 |
xp = Xrestpos(xs, xp, xpos); |
|
909 |
break; |
||
910 |
} |
||
911 |
2813006 |
ungetsc(c); |
|
912 |
✓✓✓✓ ✓✓ |
359143540 |
while ((c = getsc()) != '\n') { |
913 |
✓✓ | 69015737 |
if (c == 0) |
914 |
yyerror("here document `%s' unclosed\n", eof); |
||
915 |
✓✓ | 69125832 |
Xcheck(xs, xp); |
916 |
69015712 |
Xput(xs, xp, c); |
|
917 |
} |
||
918 |
✓✓ | 2814168 |
Xcheck(xs, xp); |
919 |
2812981 |
Xput(xs, xp, c); |
|
920 |
} |
||
921 |
233651 |
Xput(xs, xp, '\0'); |
|
922 |
233651 |
iop->heredoc = Xclose(xs, xp); |
|
923 |
|||
924 |
✓✓ | 233651 |
if (!(iop->flag & IOEVAL)) |
925 |
26141 |
ignore_backslash_newline--; |
|
926 |
233651 |
} |
|
927 |
|||
928 |
void |
||
929 |
yyerror(const char *fmt, ...) |
||
930 |
{ |
||
931 |
102 |
va_list va; |
|
932 |
|||
933 |
/* pop aliases and re-reads */ |
||
934 |
✓✗✗✓ |
204 |
while (source->type == SALIAS || source->type == SREREAD) |
935 |
source = source->next; |
||
936 |
source->str = null; /* zap pending input */ |
||
937 |
|||
938 |
error_prefix(true); |
||
939 |
va_start(va, fmt); |
||
940 |
shf_vfprintf(shl_out, fmt, va); |
||
941 |
va_end(va); |
||
942 |
errorf(NULL); |
||
943 |
} |
||
944 |
|||
945 |
/* |
||
946 |
* input for yylex with alias expansion |
||
947 |
*/ |
||
948 |
|||
949 |
Source * |
||
950 |
pushs(int type, Area *areap) |
||
951 |
{ |
||
952 |
Source *s; |
||
953 |
|||
954 |
9631294 |
s = alloc(sizeof(Source), areap); |
|
955 |
4815647 |
s->type = type; |
|
956 |
4815647 |
s->str = null; |
|
957 |
4815647 |
s->start = NULL; |
|
958 |
4815647 |
s->line = 0; |
|
959 |
4815647 |
s->cmd_offset = 0; |
|
960 |
4815647 |
s->errline = 0; |
|
961 |
4815647 |
s->file = NULL; |
|
962 |
4815647 |
s->flags = 0; |
|
963 |
4815647 |
s->next = NULL; |
|
964 |
4815647 |
s->areap = areap; |
|
965 |
✓✓ | 4815647 |
if (type == SFILE || type == SSTDIN) { |
966 |
char *dummy; |
||
967 |
49295 |
Xinit(s->xs, dummy, 256, s->areap); |
|
968 |
49295 |
} else |
|
969 |
4766352 |
memset(&s->xs, 0, sizeof(s->xs)); |
|
970 |
4815647 |
return s; |
|
971 |
} |
||
972 |
|||
973 |
static int |
||
974 |
getsc__(void) |
||
975 |
{ |
||
976 |
69563842 |
Source *s = source; |
|
977 |
int c; |
||
978 |
|||
979 |
✓✓ | 91989547 |
while ((c = *s->str++) == 0) { |
980 |
57176855 |
s->str = NULL; /* return 0 for EOF by default */ |
|
981 |
✓✗✓✓ ✓✓✗✓ |
57176855 |
switch (s->type) { |
982 |
case SEOF: |
||
983 |
8468681 |
s->str = null; |
|
984 |
8468681 |
return 0; |
|
985 |
|||
986 |
case SSTDIN: |
||
987 |
case SFILE: |
||
988 |
20076082 |
getsc_line(s); |
|
989 |
20076082 |
break; |
|
990 |
|||
991 |
case SWSTR: |
||
992 |
break; |
||
993 |
|||
994 |
case SSTRING: |
||
995 |
break; |
||
996 |
|||
997 |
case SWORDS: |
||
998 |
1168257 |
s->start = s->str = *s->u.strv++; |
|
999 |
1168257 |
s->type = SWORDSEP; |
|
1000 |
1168257 |
break; |
|
1001 |
|||
1002 |
case SWORDSEP: |
||
1003 |
✓✓ | 1167784 |
if (*s->u.strv == NULL) { |
1004 |
893266 |
s->start = s->str = "\n"; |
|
1005 |
s->type = SEOF; |
||
1006 |
893266 |
} else { |
|
1007 |
274518 |
s->start = s->str = " "; |
|
1008 |
s->type = SWORDS; |
||
1009 |
} |
||
1010 |
1167784 |
break; |
|
1011 |
|||
1012 |
case SALIAS: |
||
1013 |
✓✓ | 29175 |
if (s->flags & SF_ALIASEND) { |
1014 |
/* pass on an unused SF_ALIAS flag */ |
||
1015 |
14127 |
source = s->next; |
|
1016 |
14127 |
source->flags |= s->flags & SF_ALIAS; |
|
1017 |
14127 |
s = source; |
|
1018 |
✓✗✓✓ |
44223 |
} else if (*s->u.tblp->val.s && |
1019 |
15048 |
isspace((unsigned char)strchr(s->u.tblp->val.s, 0)[-1])) { |
|
1020 |
546 |
source = s = s->next; /* pop source stack */ |
|
1021 |
/* Note that this alias ended with a space, |
||
1022 |
* enabling alias expansion on the following |
||
1023 |
* word. |
||
1024 |
*/ |
||
1025 |
546 |
s->flags |= SF_ALIAS; |
|
1026 |
546 |
} else { |
|
1027 |
/* At this point, we need to keep the current |
||
1028 |
* alias in the source list so recursive |
||
1029 |
* aliases can be detected and we also need |
||
1030 |
* to return the next character. Do this |
||
1031 |
* by temporarily popping the alias to get |
||
1032 |
* the next character and then put it back |
||
1033 |
* in the source list with the SF_ALIASEND |
||
1034 |
* flag set. |
||
1035 |
*/ |
||
1036 |
14502 |
source = s->next; /* pop source stack */ |
|
1037 |
14502 |
source->flags |= s->flags & SF_ALIAS; |
|
1038 |
14502 |
c = getsc__(); |
|
1039 |
✓✓ | 14502 |
if (c) { |
1040 |
14452 |
s->flags |= SF_ALIASEND; |
|
1041 |
14452 |
s->ugbuf[0] = c; s->ugbuf[1] = '\0'; |
|
1042 |
14452 |
s->start = s->str = s->ugbuf; |
|
1043 |
14452 |
s->next = source; |
|
1044 |
14452 |
source = s; |
|
1045 |
} else { |
||
1046 |
50 |
s = source; |
|
1047 |
/* avoid reading eof twice */ |
||
1048 |
50 |
s->str = NULL; |
|
1049 |
50 |
break; |
|
1050 |
} |
||
1051 |
} |
||
1052 |
continue; |
||
1053 |
|||
1054 |
case SREREAD: |
||
1055 |
if (s->start != s->ugbuf) /* yuck */ |
||
1056 |
afree(s->u.freeme, ATEMP); |
||
1057 |
source = s = s->next; |
||
1058 |
continue; |
||
1059 |
} |
||
1060 |
✓✓ | 26266876 |
if (s->str == NULL) { |
1061 |
3870296 |
s->type = SEOF; |
|
1062 |
3870296 |
s->start = s->str = null; |
|
1063 |
3870296 |
return '\0'; |
|
1064 |
} |
||
1065 |
✓✓ | 22396580 |
if (s->flags & SF_ECHO) { |
1066 |
25 |
shf_puts(s->str, shl_out); |
|
1067 |
25 |
shf_flush(shl_out); |
|
1068 |
25 |
} |
|
1069 |
} |
||
1070 |
22440082 |
return c; |
|
1071 |
34779059 |
} |
|
1072 |
|||
1073 |
static void |
||
1074 |
getsc_line(Source *s) |
||
1075 |
{ |
||
1076 |
40152164 |
char *xp = Xstring(s->xs, xp); |
|
1077 |
✓✓ | 40162024 |
int interactive = Flag(FTALKING) && s->type == SSTDIN; |
1078 |
✓✓ | 40159752 |
int have_tty = interactive && (s->flags & SF_TTY); |
1079 |
|||
1080 |
/* Done here to ensure nothing odd happens when a timeout occurs */ |
||
1081 |
✓✓ | 20125375 |
XcheckN(s->xs, xp, LINE); |
1082 |
20076082 |
*xp = '\0'; |
|
1083 |
20076082 |
s->start = s->str = xp; |
|
1084 |
|||
1085 |
✗✓ | 20076082 |
if (have_tty && ksh_tmout) { |
1086 |
ksh_tmout_state = TMOUT_READING; |
||
1087 |
alarm(ksh_tmout); |
||
1088 |
} |
||
1089 |
#ifdef EDIT |
||
1090 |
✓✓✗✗ |
20076082 |
if (have_tty && (0 |
1091 |
# ifdef VI |
||
1092 |
3588 |
|| Flag(FVI) |
|
1093 |
# endif /* VI */ |
||
1094 |
# ifdef EMACS |
||
1095 |
✓✓✗✓ |
5401 |
|| Flag(FEMACS) || Flag(FGMACS) |
1096 |
# endif /* EMACS */ |
||
1097 |
)) { |
||
1098 |
int nread; |
||
1099 |
|||
1100 |
3588 |
nread = x_read(xp, LINE); |
|
1101 |
3588 |
if (nread < 0) /* read error */ |
|
1102 |
nread = 0; |
||
1103 |
3588 |
xp[nread] = '\0'; |
|
1104 |
xp += nread; |
||
1105 |
3588 |
} |
|
1106 |
else |
||
1107 |
#endif /* EDIT */ |
||
1108 |
{ |
||
1109 |
✓✓ | 20072494 |
if (interactive) { |
1110 |
4000 |
pprompt(prompt, 0); |
|
1111 |
4000 |
} else |
|
1112 |
20068494 |
s->line++; |
|
1113 |
|||
1114 |
while (1) { |
||
1115 |
20072594 |
char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf); |
|
1116 |
|||
1117 |
✓✓✗✓ ✗✗ |
20085034 |
if (!p && shf_error(s->u.shf) && |
1118 |
s->u.shf->errno_ == EINTR) { |
||
1119 |
shf_clearerr(s->u.shf); |
||
1120 |
if (trap) |
||
1121 |
runtraps(0); |
||
1122 |
continue; |
||
1123 |
} |
||
1124 |
✓✓✓✓ |
40132748 |
if (!p || (xp = p, xp[-1] == '\n')) |
1125 |
20072494 |
break; |
|
1126 |
/* double buffer size */ |
||
1127 |
100 |
xp++; /* move past null so doubling works... */ |
|
1128 |
✓✓ | 125 |
XcheckN(s->xs, xp, Xlength(s->xs, xp)); |
1129 |
100 |
xp--; /* ...and move back again */ |
|
1130 |
✓✓ | 100 |
} |
1131 |
/* flush any unwanted input so other programs/builtins |
||
1132 |
* can read it. Not very optimal, but less error prone |
||
1133 |
* than flushing else where, dealing with redirections, |
||
1134 |
* etc.. |
||
1135 |
* todo: reduce size of shf buffer (~128?) if SSTDIN |
||
1136 |
*/ |
||
1137 |
✓✓ | 20072494 |
if (s->type == SSTDIN) |
1138 |
67842 |
shf_flush(s->u.shf); |
|
1139 |
} |
||
1140 |
/* XXX: temporary kludge to restore source after a |
||
1141 |
* trap may have been executed. |
||
1142 |
*/ |
||
1143 |
20073220 |
source = s; |
|
1144 |
✗✓ | 20073220 |
if (have_tty && ksh_tmout) { |
1145 |
ksh_tmout_state = TMOUT_EXECUTING; |
||
1146 |
alarm(0); |
||
1147 |
} |
||
1148 |
20073220 |
s->start = s->str = Xstring(s->xs, xp); |
|
1149 |
20073220 |
strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp)); |
|
1150 |
/* Note: if input is all nulls, this is not eof */ |
||
1151 |
✓✓ | 20073220 |
if (Xlength(s->xs, xp) == 0) { /* EOF */ |
1152 |
✓✓ | 12381 |
if (s->type == SFILE) |
1153 |
7251 |
shf_fdclose(s->u.shf); |
|
1154 |
12381 |
s->str = NULL; |
|
1155 |
✓✓ | 20073220 |
} else if (interactive) { |
1156 |
#ifdef HISTORY |
||
1157 |
char *p = Xstring(s->xs, xp); |
||
1158 |
✓✓ | 3960 |
if (cur_prompt == PS1) |
1159 |
✓✓✓✓ ✓✗ |
8348 |
while (*p && ctype(*p, C_IFS) && ctype(*p, C_IFSWS)) |
1160 |
157 |
p++; |
|
1161 |
✓✓ | 3960 |
if (*p) { |
1162 |
3860 |
s->line++; |
|
1163 |
3860 |
histsave(s->line, s->str, 1); |
|
1164 |
3860 |
} |
|
1165 |
#endif /* HISTORY */ |
||
1166 |
3960 |
} |
|
1167 |
✓✓ | 20073220 |
if (interactive) |
1168 |
4726 |
set_prompt(PS2, NULL); |
|
1169 |
20073220 |
} |
|
1170 |
|||
1171 |
static char * |
||
1172 |
special_prompt_expand(char *str) |
||
1173 |
{ |
||
1174 |
char *p = str; |
||
1175 |
|||
1176 |
✗✓ | 22614 |
while ((p = strstr(p, "\\$")) != NULL) { |
1177 |
*(p+1) = 'p'; |
||
1178 |
} |
||
1179 |
7538 |
return str; |
|
1180 |
} |
||
1181 |
|||
1182 |
void |
||
1183 |
set_prompt(int to, Source *s) |
||
1184 |
{ |
||
1185 |
char *ps1; |
||
1186 |
Area *saved_atemp; |
||
1187 |
|||
1188 |
36792 |
cur_prompt = to; |
|
1189 |
|||
1190 |
✓✓✓ | 24528 |
switch (to) { |
1191 |
case PS1: /* command */ |
||
1192 |
7538 |
ps1 = str_save(str_val(global("PS1")), ATEMP); |
|
1193 |
7538 |
saved_atemp = ATEMP; /* ps1 is freed by substitute() */ |
|
1194 |
7538 |
newenv(E_ERRH); |
|
1195 |
✗✓ | 7538 |
if (sigsetjmp(genv->jbuf, 0)) { |
1196 |
prompt = safe_prompt; |
||
1197 |
/* Don't print an error - assume it has already |
||
1198 |
* been printed. Reason is we may have forked |
||
1199 |
* to run a command and the child may be |
||
1200 |
* unwinding its stack through this code as it |
||
1201 |
* exits. |
||
1202 |
*/ |
||
1203 |
} else { |
||
1204 |
/* expand \$ before other substitutions are done */ |
||
1205 |
7538 |
char *tmp = special_prompt_expand(ps1); |
|
1206 |
7538 |
prompt = str_save(substitute(tmp, 0), saved_atemp); |
|
1207 |
} |
||
1208 |
7538 |
quitenv(NULL); |
|
1209 |
7538 |
break; |
|
1210 |
case PS2: /* command continuation */ |
||
1211 |
4726 |
prompt = str_val(global("PS2")); |
|
1212 |
4726 |
break; |
|
1213 |
} |
||
1214 |
12264 |
} |
|
1215 |
|||
1216 |
static int |
||
1217 |
dopprompt(const char *sp, int ntruncate, const char **spp, int doprint) |
||
1218 |
{ |
||
1219 |
27566 |
char strbuf[1024], tmpbuf[1024], *p, *str, nbuf[32], delimiter = '\0'; |
|
1220 |
int len, c, n, totlen = 0, indelimit = 0, counting = 1, delimitthis; |
||
1221 |
const char *cp = sp; |
||
1222 |
struct tm *tm; |
||
1223 |
13783 |
time_t t; |
|
1224 |
|||
1225 |
✓✓✗✓ |
27391 |
if (*cp && cp[1] == '\r') { |
1226 |
delimiter = *cp; |
||
1227 |
cp += 2; |
||
1228 |
} |
||
1229 |
|||
1230 |
✓✓ | 43374 |
while (*cp != 0) { |
1231 |
delimitthis = 0; |
||
1232 |
✗✓✗✗ |
29591 |
if (indelimit && *cp != delimiter) |
1233 |
; |
||
1234 |
✓✗✗✓ |
59182 |
else if (*cp == '\n' || *cp == '\r') { |
1235 |
totlen = 0; |
||
1236 |
sp = cp + 1; |
||
1237 |
✗✓ | 29591 |
} else if (*cp == '\t') { |
1238 |
if (counting) |
||
1239 |
totlen = (totlen | 7) + 1; |
||
1240 |
✗✓ | 29591 |
} else if (*cp == delimiter) { |
1241 |
indelimit = !indelimit; |
||
1242 |
delimitthis = 1; |
||
1243 |
} |
||
1244 |
|||
1245 |
✓✓ | 29591 |
if (*cp == '\\') { |
1246 |
125 |
cp++; |
|
1247 |
✓✗ | 125 |
if (!*cp) |
1248 |
break; |
||
1249 |
✗✓ | 125 |
if (Flag(FSH)) |
1250 |
snprintf(strbuf, sizeof strbuf, "\\%c", *cp); |
||
1251 |
✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✓✗ ✗✗✗ |
125 |
else switch (*cp) { |
1252 |
case 'a': /* '\' 'a' bell */ |
||
1253 |
strbuf[0] = '\007'; |
||
1254 |
strbuf[1] = '\0'; |
||
1255 |
break; |
||
1256 |
case 'd': /* '\' 'd' Dow Mon DD */ |
||
1257 |
time(&t); |
||
1258 |
tm = localtime(&t); |
||
1259 |
strftime(strbuf, sizeof strbuf, "%a %b %d", tm); |
||
1260 |
break; |
||
1261 |
case 'D': /* '\' 'D' '{' strftime format '}' */ |
||
1262 |
p = strchr(cp + 2, '}'); |
||
1263 |
if (cp[1] != '{' || p == NULL) { |
||
1264 |
snprintf(strbuf, sizeof strbuf, |
||
1265 |
"\\%c", *cp); |
||
1266 |
break; |
||
1267 |
} |
||
1268 |
strlcpy(tmpbuf, cp + 2, sizeof tmpbuf); |
||
1269 |
p = strchr(tmpbuf, '}'); |
||
1270 |
if (p) |
||
1271 |
*p = '\0'; |
||
1272 |
time(&t); |
||
1273 |
tm = localtime(&t); |
||
1274 |
strftime(strbuf, sizeof strbuf, tmpbuf, tm); |
||
1275 |
cp = strchr(cp + 2, '}'); |
||
1276 |
break; |
||
1277 |
case 'e': /* '\' 'e' escape */ |
||
1278 |
strbuf[0] = '\033'; |
||
1279 |
strbuf[1] = '\0'; |
||
1280 |
break; |
||
1281 |
case 'h': /* '\' 'h' shortened hostname */ |
||
1282 |
gethostname(strbuf, sizeof strbuf); |
||
1283 |
p = strchr(strbuf, '.'); |
||
1284 |
if (p) |
||
1285 |
*p = '\0'; |
||
1286 |
break; |
||
1287 |
case 'H': /* '\' 'H' full hostname */ |
||
1288 |
gethostname(strbuf, sizeof strbuf); |
||
1289 |
break; |
||
1290 |
case 'j': /* '\' 'j' number of jobs */ |
||
1291 |
snprintf(strbuf, sizeof strbuf, "%d", |
||
1292 |
j_njobs()); |
||
1293 |
break; |
||
1294 |
case 'l': /* '\' 'l' basename of tty */ |
||
1295 |
p = ttyname(0); |
||
1296 |
if (p) |
||
1297 |
p = basename(p); |
||
1298 |
if (p) |
||
1299 |
strlcpy(strbuf, p, sizeof strbuf); |
||
1300 |
break; |
||
1301 |
case 'n': /* '\' 'n' newline */ |
||
1302 |
strbuf[0] = '\n'; |
||
1303 |
strbuf[1] = '\0'; |
||
1304 |
totlen = 0; /* reset for prompt re-print */ |
||
1305 |
sp = cp + 1; |
||
1306 |
break; |
||
1307 |
case 'p': /* '\' '$' $ or # */ |
||
1308 |
strbuf[0] = ksheuid ? '$' : '#'; |
||
1309 |
strbuf[1] = '\0'; |
||
1310 |
break; |
||
1311 |
case 'r': /* '\' 'r' return */ |
||
1312 |
strbuf[0] = '\r'; |
||
1313 |
strbuf[1] = '\0'; |
||
1314 |
totlen = 0; /* reset for prompt re-print */ |
||
1315 |
sp = cp + 1; |
||
1316 |
break; |
||
1317 |
case 's': /* '\' 's' basename $0 */ |
||
1318 |
strlcpy(strbuf, kshname, sizeof strbuf); |
||
1319 |
break; |
||
1320 |
case 't': /* '\' 't' 24 hour HH:MM:SS */ |
||
1321 |
time(&t); |
||
1322 |
tm = localtime(&t); |
||
1323 |
strftime(strbuf, sizeof strbuf, "%T", tm); |
||
1324 |
break; |
||
1325 |
case 'T': /* '\' 'T' 12 hour HH:MM:SS */ |
||
1326 |
time(&t); |
||
1327 |
tm = localtime(&t); |
||
1328 |
strftime(strbuf, sizeof strbuf, "%l:%M:%S", tm); |
||
1329 |
break; |
||
1330 |
case '@': /* '\' '@' 12 hour am/pm format */ |
||
1331 |
time(&t); |
||
1332 |
tm = localtime(&t); |
||
1333 |
strftime(strbuf, sizeof strbuf, "%r", tm); |
||
1334 |
break; |
||
1335 |
case 'A': /* '\' 'A' 24 hour HH:MM */ |
||
1336 |
time(&t); |
||
1337 |
tm = localtime(&t); |
||
1338 |
strftime(strbuf, sizeof strbuf, "%R", tm); |
||
1339 |
break; |
||
1340 |
case 'u': /* '\' 'u' username */ |
||
1341 |
strlcpy(strbuf, username, sizeof strbuf); |
||
1342 |
break; |
||
1343 |
case 'v': /* '\' 'v' version (short) */ |
||
1344 |
p = strchr(ksh_version, ' '); |
||
1345 |
if (p) |
||
1346 |
p = strchr(p + 1, ' '); |
||
1347 |
if (p) { |
||
1348 |
p++; |
||
1349 |
strlcpy(strbuf, p, sizeof strbuf); |
||
1350 |
p = strchr(strbuf, ' '); |
||
1351 |
if (p) |
||
1352 |
*p = '\0'; |
||
1353 |
} |
||
1354 |
break; |
||
1355 |
case 'V': /* '\' 'V' version (long) */ |
||
1356 |
strlcpy(strbuf, ksh_version, sizeof strbuf); |
||
1357 |
break; |
||
1358 |
case 'w': /* '\' 'w' cwd */ |
||
1359 |
p = str_val(global("PWD")); |
||
1360 |
n = strlen(str_val(global("HOME"))); |
||
1361 |
if (strcmp(p, "/") == 0) { |
||
1362 |
strlcpy(strbuf, p, sizeof strbuf); |
||
1363 |
} else if (strcmp(p, str_val(global("HOME"))) == 0) { |
||
1364 |
strbuf[0] = '~'; |
||
1365 |
strbuf[1] = '\0'; |
||
1366 |
} else if (strncmp(p, str_val(global("HOME")), n) |
||
1367 |
== 0 && p[n] == '/') { |
||
1368 |
snprintf(strbuf, sizeof strbuf, "~/%s", |
||
1369 |
str_val(global("PWD")) + n + 1); |
||
1370 |
} else |
||
1371 |
strlcpy(strbuf, p, sizeof strbuf); |
||
1372 |
break; |
||
1373 |
case 'W': /* '\' 'W' basename(cwd) */ |
||
1374 |
p = str_val(global("PWD")); |
||
1375 |
if (strcmp(p, str_val(global("HOME"))) == 0) { |
||
1376 |
strbuf[0] = '~'; |
||
1377 |
strbuf[1] = '\0'; |
||
1378 |
} else |
||
1379 |
strlcpy(strbuf, basename(p), sizeof strbuf); |
||
1380 |
break; |
||
1381 |
case '!': /* '\' '!' history line number */ |
||
1382 |
snprintf(strbuf, sizeof strbuf, "%d", |
||
1383 |
source->line + 1); |
||
1384 |
break; |
||
1385 |
case '#': /* '\' '#' command line number */ |
||
1386 |
snprintf(strbuf, sizeof strbuf, "%d", |
||
1387 |
source->line - source->cmd_offset + 1); |
||
1388 |
break; |
||
1389 |
case '0': /* '\' '#' '#' ' #' octal numeric handling */ |
||
1390 |
case '1': |
||
1391 |
case '2': |
||
1392 |
case '3': |
||
1393 |
case '4': |
||
1394 |
case '5': |
||
1395 |
case '6': |
||
1396 |
case '7': |
||
1397 |
✓✗✓✗ ✗✓ |
375 |
if ((cp[1] > '7' || cp[1] < '0') || |
1398 |
✓✗ | 250 |
(cp[2] > '7' || cp[2] < '0')) { |
1399 |
snprintf(strbuf, sizeof strbuf, |
||
1400 |
"\\%c", *cp); |
||
1401 |
break; |
||
1402 |
} |
||
1403 |
250 |
n = (cp[0] - '0') * 8 * 8 + (cp[1] - '0') * 8 + |
|
1404 |
125 |
(cp[2] - '0'); |
|
1405 |
125 |
snprintf(strbuf, sizeof strbuf, "%c", n); |
|
1406 |
cp += 2; |
||
1407 |
125 |
break; |
|
1408 |
case '\\': /* '\' '\' */ |
||
1409 |
strbuf[0] = '\\'; |
||
1410 |
strbuf[1] = '\0'; |
||
1411 |
break; |
||
1412 |
case '[': /* '\' '[' .... stop counting */ |
||
1413 |
strbuf[0] = '\0'; |
||
1414 |
counting = 0; |
||
1415 |
break; |
||
1416 |
case ']': /* '\' ']' restart counting */ |
||
1417 |
strbuf[0] = '\0'; |
||
1418 |
counting = 1; |
||
1419 |
break; |
||
1420 |
|||
1421 |
default: |
||
1422 |
snprintf(strbuf, sizeof strbuf, "\\%c", *cp); |
||
1423 |
break; |
||
1424 |
} |
||
1425 |
125 |
cp++; |
|
1426 |
|||
1427 |
125 |
str = strbuf; |
|
1428 |
125 |
len = strlen(str); |
|
1429 |
✗✓ | 125 |
if (ntruncate) { |
1430 |
if (ntruncate >= len) { |
||
1431 |
ntruncate -= len; |
||
1432 |
continue; |
||
1433 |
} |
||
1434 |
str += ntruncate; |
||
1435 |
len -= ntruncate; |
||
1436 |
ntruncate = 0; |
||
1437 |
} |
||
1438 |
✓✗ | 125 |
if (doprint) |
1439 |
125 |
shf_write(str, len, shl_out); |
|
1440 |
✗✓ | 125 |
if (counting && !indelimit && !delimitthis) |
1441 |
125 |
totlen += len; |
|
1442 |
continue; |
||
1443 |
✓✗ | 29466 |
} else if (*cp != '!') |
1444 |
29466 |
c = *cp++; |
|
1445 |
else if (*++cp == '!') |
||
1446 |
c = *cp++; |
||
1447 |
else { |
||
1448 |
char *p; |
||
1449 |
|||
1450 |
shf_snprintf(p = nbuf, sizeof(nbuf), "%d", |
||
1451 |
source->line + 1); |
||
1452 |
len = strlen(nbuf); |
||
1453 |
if (ntruncate) { |
||
1454 |
if (ntruncate >= len) { |
||
1455 |
ntruncate -= len; |
||
1456 |
continue; |
||
1457 |
} |
||
1458 |
p += ntruncate; |
||
1459 |
len -= ntruncate; |
||
1460 |
ntruncate = 0; |
||
1461 |
} |
||
1462 |
if (doprint) |
||
1463 |
shf_write(p, len, shl_out); |
||
1464 |
if (counting && !indelimit && !delimitthis) |
||
1465 |
totlen += len; |
||
1466 |
continue; |
||
1467 |
} |
||
1468 |
✗✓ | 29466 |
if (counting && ntruncate) |
1469 |
--ntruncate; |
||
1470 |
✓✓ | 29466 |
else if (doprint) { |
1471 |
✓✓ | 34166 |
shf_putc(c, shl_out); |
1472 |
} |
||
1473 |
✗✓ | 29466 |
if (counting && !indelimit && !delimitthis) |
1474 |
29466 |
totlen++; |
|
1475 |
} |
||
1476 |
✓✓ | 13783 |
if (doprint) |
1477 |
9004 |
shf_flush(shl_out); |
|
1478 |
✓✓ | 13783 |
if (spp) |
1479 |
3588 |
*spp = sp; |
|
1480 |
13783 |
return (totlen); |
|
1481 |
13783 |
} |
|
1482 |
|||
1483 |
void |
||
1484 |
pprompt(const char *cp, int ntruncate) |
||
1485 |
{ |
||
1486 |
18008 |
dopprompt(cp, ntruncate, NULL, 1); |
|
1487 |
9004 |
} |
|
1488 |
|||
1489 |
int |
||
1490 |
promptlen(const char *cp, const char **spp) |
||
1491 |
{ |
||
1492 |
9558 |
return dopprompt(cp, 0, spp, 0); |
|
1493 |
} |
||
1494 |
|||
1495 |
/* Read the variable part of a ${...} expression (ie, up to but not including |
||
1496 |
* the :[-+?=#%] or close-brace. |
||
1497 |
*/ |
||
1498 |
static char * |
||
1499 |
get_brace_var(XString *wsp, char *wp) |
||
1500 |
{ |
||
1501 |
enum parse_state { |
||
1502 |
PS_INITIAL, PS_SAW_HASH, PS_IDENT, |
||
1503 |
PS_NUMBER, PS_VAR1, PS_END |
||
1504 |
} |
||
1505 |
state; |
||
1506 |
char c; |
||
1507 |
|||
1508 |
state = PS_INITIAL; |
||
1509 |
20894225 |
while (1) { |
|
1510 |
✓✗✓✓ |
94315240 |
c = getsc(); |
1511 |
/* State machine to figure out where the variable part ends. */ |
||
1512 |
✓✓✓✓ ✓✓ |
39249370 |
switch (state) { |
1513 |
case PS_INITIAL: |
||
1514 |
✓✓ | 2538935 |
if (c == '#') { |
1515 |
state = PS_SAW_HASH; |
||
1516 |
145 |
break; |
|
1517 |
} |
||
1518 |
/* FALLTHROUGH */ |
||
1519 |
case PS_SAW_HASH: |
||
1520 |
✓✓ | 2538935 |
if (letter(c)) |
1521 |
2513658 |
state = PS_IDENT; |
|
1522 |
✓✓ | 25277 |
else if (digit(c)) |
1523 |
25225 |
state = PS_NUMBER; |
|
1524 |
✓✓ | 52 |
else if (ctype(c, C_VAR1)) |
1525 |
27 |
state = PS_VAR1; |
|
1526 |
else |
||
1527 |
state = PS_END; |
||
1528 |
break; |
||
1529 |
case PS_IDENT: |
||
1530 |
✓✓✓✓ |
18980529 |
if (!letnum(c)) { |
1531 |
state = PS_END; |
||
1532 |
✓✓ | 2513658 |
if (c == '[') { |
1533 |
1230 |
char *tmp, *p; |
|
1534 |
|||
1535 |
✗✓ | 1230 |
if (!arraysub(&tmp)) |
1536 |
yyerror("missing ]\n"); |
||
1537 |
1230 |
*wp++ = c; |
|
1538 |
✓✓ | 11104 |
for (p = tmp; *p; ) { |
1539 |
✗✓ | 4322 |
Xcheck(*wsp, wp); |
1540 |
4322 |
*wp++ = *p++; |
|
1541 |
} |
||
1542 |
1230 |
afree(tmp, ATEMP); |
|
1543 |
✓✗✓✗ |
4920 |
c = getsc(); /* the ] */ |
1544 |
1230 |
} |
|
1545 |
} |
||
1546 |
break; |
||
1547 |
case PS_NUMBER: |
||
1548 |
✓✗ | 25225 |
if (!digit(c)) |
1549 |
25225 |
state = PS_END; |
|
1550 |
break; |
||
1551 |
case PS_VAR1: |
||
1552 |
state = PS_END; |
||
1553 |
27 |
break; |
|
1554 |
case PS_END: /* keep gcc happy */ |
||
1555 |
break; |
||
1556 |
} |
||
1557 |
✓✓ | 18355290 |
if (state == PS_END) { |
1558 |
2538935 |
*wp++ = '\0'; /* end of variable part */ |
|
1559 |
2538935 |
ungetsc(c); |
|
1560 |
break; |
||
1561 |
} |
||
1562 |
✓✓ | 15824873 |
Xcheck(*wsp, wp); |
1563 |
15816355 |
*wp++ = c; |
|
1564 |
} |
||
1565 |
2538935 |
return wp; |
|
1566 |
} |
||
1567 |
|||
1568 |
/* |
||
1569 |
* Save an array subscript - returns true if matching bracket found, false |
||
1570 |
* if eof or newline was found. |
||
1571 |
* (Returned string double null terminated) |
||
1572 |
*/ |
||
1573 |
static int |
||
1574 |
arraysub(char **strp) |
||
1575 |
{ |
||
1576 |
4106 |
XString ws; |
|
1577 |
char *wp; |
||
1578 |
char c; |
||
1579 |
int depth = 1; /* we are just past the initial [ */ |
||
1580 |
|||
1581 |
2053 |
Xinit(ws, wp, 32, ATEMP); |
|
1582 |
|||
1583 |
2053 |
do { |
|
1584 |
✓✗✓✗ |
41172 |
c = getsc(); |
1585 |
✗✓ | 10293 |
Xcheck(ws, wp); |
1586 |
10293 |
*wp++ = c; |
|
1587 |
✓✓ | 10293 |
if (c == '[') |
1588 |
420 |
depth++; |
|
1589 |
✓✓ | 9873 |
else if (c == ']') |
1590 |
2473 |
depth--; |
|
1591 |
✓✓✓✗ ✓✗ |
26773 |
} while (depth > 0 && c && c != '\n'); |
1592 |
|||
1593 |
2053 |
*wp++ = '\0'; |
|
1594 |
2053 |
*strp = Xclose(ws, wp); |
|
1595 |
|||
1596 |
4106 |
return depth == 0 ? 1 : 0; |
|
1597 |
2053 |
} |
|
1598 |
|||
1599 |
/* Unget a char: handles case when we are already at the start of the buffer */ |
||
1600 |
static const char * |
||
1601 |
ungetsc(int c) |
||
1602 |
{ |
||
1603 |
✓✓ | 346913404 |
if (backslash_skip) |
1604 |
552179 |
backslash_skip--; |
|
1605 |
/* Don't unget eof... */ |
||
1606 |
✓✓ | 173456702 |
if (source->str == null && c == '\0') |
1607 |
7445791 |
return source->str; |
|
1608 |
✓✗ | 166010911 |
if (source->str > source->start) |
1609 |
166010911 |
source->str--; |
|
1610 |
else { |
||
1611 |
Source *s; |
||
1612 |
|||
1613 |
s = pushs(SREREAD, source->areap); |
||
1614 |
s->ugbuf[0] = c; s->ugbuf[1] = '\0'; |
||
1615 |
s->start = s->str = s->ugbuf; |
||
1616 |
s->next = source; |
||
1617 |
source = s; |
||
1618 |
} |
||
1619 |
166010911 |
return source->str; |
|
1620 |
173456702 |
} |
|
1621 |
|||
1622 |
|||
1623 |
/* Called to get a char that isn't a \newline sequence. */ |
||
1624 |
static int |
||
1625 |
getsc_bn(void) |
||
1626 |
{ |
||
1627 |
int c, c2; |
||
1628 |
|||
1629 |
✓✓ | 98275192 |
if (ignore_backslash_newline) |
1630 |
✓✓ | 6154368 |
return getsc_(); |
1631 |
|||
1632 |
✓✓ | 47086140 |
if (backslash_skip == 1) { |
1633 |
4642788 |
backslash_skip = 2; |
|
1634 |
✓✓ | 13928364 |
return getsc_(); |
1635 |
} |
||
1636 |
|||
1637 |
42443352 |
backslash_skip = 0; |
|
1638 |
|||
1639 |
42443352 |
while (1) { |
|
1640 |
✓✓ | 128726700 |
c = getsc_(); |
1641 |
✓✓ | 42906992 |
if (c == '\\') { |
1642 |
✓✓✓✓ |
16984407 |
if ((c2 = getsc_()) == '\n') |
1643 |
/* ignore the \newline; get the next char... */ |
||
1644 |
continue; |
||
1645 |
5194967 |
ungetsc(c2); |
|
1646 |
5194967 |
backslash_skip = 1; |
|
1647 |
5194967 |
} |
|
1648 |
42440490 |
return c; |
|
1649 |
} |
||
1650 |
49134734 |
} |
|
1651 |
|||
1652 |
static Lex_state * |
||
1653 |
push_state_(State_info *si, Lex_state *old_end) |
||
1654 |
{ |
||
1655 |
Lex_state *new = areallocarray(NULL, STATE_BSIZE, |
||
1656 |
sizeof(Lex_state), ATEMP); |
||
1657 |
|||
1658 |
new[0].ls_info.base = old_end; |
||
1659 |
si->base = &new[0]; |
||
1660 |
si->end = &new[STATE_BSIZE]; |
||
1661 |
return &new[1]; |
||
1662 |
} |
||
1663 |
|||
1664 |
static Lex_state * |
||
1665 |
pop_state_(State_info *si, Lex_state *old_end) |
||
1666 |
{ |
||
1667 |
Lex_state *old_base = si->base; |
||
1668 |
|||
1669 |
si->base = old_end->ls_info.base - STATE_BSIZE; |
||
1670 |
si->end = old_end->ls_info.base; |
||
1671 |
|||
1672 |
afree(old_base, ATEMP); |
||
1673 |
|||
1674 |
return si->base + STATE_BSIZE - 1; |
||
1675 |
} |
Generated by: GCOVR (Version 3.3) |