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 |
37713766 |
Lex_state states[STATE_BSIZE], *statep; |
|
139 |
18856883 |
State_info state_info; |
|
140 |
int c, state; |
||
141 |
18856883 |
XString ws; /* expandable output word */ |
|
142 |
char *wp; /* output word pointer */ |
||
143 |
char *sp, *dp; |
||
144 |
18856883 |
int c2; |
|
145 |
|||
146 |
|||
147 |
Again: |
||
148 |
18882018 |
states[0].ls_state = SINVALID; |
|
149 |
18882018 |
states[0].ls_info.base = NULL; |
|
150 |
18882018 |
statep = &states[1]; |
|
151 |
18882018 |
state_info.base = states; |
|
152 |
18882018 |
state_info.end = &states[STATE_BSIZE]; |
|
153 |
|||
154 |
18882018 |
Xinit(ws, wp, 64, ATEMP); |
|
155 |
|||
156 |
18882018 |
backslash_skip = 0; |
|
157 |
18882018 |
ignore_backslash_newline = 0; |
|
158 |
|||
159 |
✓✓ | 18882018 |
if (cf&ONEWORD) |
160 |
262492 |
state = SWORD; |
|
161 |
✓✓ | 18619526 |
else if (cf&LETEXPR) { |
162 |
12 |
*wp++ = OQUOTE; /* enclose arguments in (double) quotes */ |
|
163 |
state = SLETPAREN; |
||
164 |
12 |
statep->ls_sletparen.nparen = 0; |
|
165 |
12 |
} else { /* normal lexing */ |
|
166 |
18619514 |
state = (cf & HEREDELIM) ? SHEREDELIM : SBASE; |
|
167 |
✓✓✓✓ ✓✓ |
158938796 |
while ((c = getsc()) == ' ' || c == '\t') |
168 |
; |
||
169 |
✓✓ | 18618859 |
if (c == '#') { |
170 |
375215 |
ignore_backslash_newline++; |
|
171 |
✓✗✓✓ ✓✓ |
82034345 |
while ((c = getsc()) != '\0' && c != '\n') |
172 |
; |
||
173 |
375215 |
ignore_backslash_newline--; |
|
174 |
375215 |
} |
|
175 |
18618859 |
ungetsc(c); |
|
176 |
} |
||
177 |
✓✓ | 18881363 |
if (source->flags & SF_ALIAS) { /* trailing ' ' in alias definition */ |
178 |
260 |
source->flags &= ~SF_ALIAS; |
|
179 |
/* In POSIX mode, a trailing space only counts if we are |
||
180 |
* parsing a simple command |
||
181 |
*/ |
||
182 |
✗✓✗✗ |
260 |
if (!Flag(FPOSIX) || (cf & CMDWORD)) |
183 |
260 |
cf |= ALIAS; |
|
184 |
} |
||
185 |
|||
186 |
/* Initial state: one of SBASE SHEREDELIM SWORD SASPAREN */ |
||
187 |
18881363 |
statep->ls_state = state; |
|
188 |
|||
189 |
/* collect non-special or quoted characters to form word */ |
||
190 |
✓✓✓✓ ✓✓✓✓ |
526358985 |
while (!((c = getsc()) == 0 || |
191 |
✓✓ | 146272581 |
((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) { |
192 |
✓✓ | 69639434 |
Xcheck(ws, wp); |
193 |
✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✓ |
89197944 |
switch (state) { |
194 |
case SBASE: |
||
195 |
✗✓✗✗ |
45792006 |
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 |
✓✓✓✓ |
45837503 |
if (c == '[' && (cf & (VARASN|ARRAYVAR))) { |
262 |
44490 |
*wp = EOS; /* temporary */ |
|
263 |
✓✓ | 44490 |
if (is_wdvarname(Xstring(ws, wp), false)) { |
264 |
509 |
char *p, *tmp; |
|
265 |
|||
266 |
✓✗ | 509 |
if (arraysub(&tmp)) { |
267 |
509 |
*wp++ = CHAR; |
|
268 |
509 |
*wp++ = c; |
|
269 |
✓✓ | 9414 |
for (p = tmp; *p; ) { |
270 |
✗✓ | 4198 |
Xcheck(ws, wp); |
271 |
4198 |
*wp++ = CHAR; |
|
272 |
4198 |
*wp++ = *p++; |
|
273 |
} |
||
274 |
509 |
afree(tmp, ATEMP); |
|
275 |
509 |
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 |
✗✓ | 509 |
} |
287 |
43981 |
*wp++ = CHAR; |
|
288 |
43981 |
*wp++ = c; |
|
289 |
43981 |
break; |
|
290 |
} |
||
291 |
/* FALLTHROUGH */ |
||
292 |
Sbase1: /* includes *(...|...) pattern (*+?@!) */ |
||
293 |
✓✓ | 94391860 |
if (c == '*' || c == '@' || c == '+' || c == '?' || |
294 |
47195930 |
c == '!') { |
|
295 |
✓✓✓✓ |
982951 |
c2 = getsc(); |
296 |
✓✓ | 245945 |
if (c2 == '(' /*)*/ ) { |
297 |
474 |
*wp++ = OPAT; |
|
298 |
474 |
*wp++ = c; |
|
299 |
✗✓ | 948 |
PUSH_STATE(SPATTERN); |
300 |
474 |
break; |
|
301 |
} |
||
302 |
245471 |
ungetsc(c2); |
|
303 |
245471 |
} |
|
304 |
/* FALLTHROUGH */ |
||
305 |
Sbase2: /* doesn't include *(...|...) pattern (*+?@!) */ |
||
306 |
✓✓✓✓ |
48037124 |
switch (c) { |
307 |
case '\\': |
||
308 |
✓✓✗✓ |
35114 |
c = getsc(); |
309 |
✓✓ | 8783 |
if (c) /* trailing \ is lost */ |
310 |
8765 |
*wp++ = QCHAR, *wp++ = c; |
|
311 |
break; |
||
312 |
case '\'': |
||
313 |
✓✓ | 250642 |
if ((cf & HEREDOC) || state == SBRACEQ) { |
314 |
48 |
*wp++ = CHAR, *wp++ = c; |
|
315 |
48 |
break; |
|
316 |
} |
||
317 |
250594 |
*wp++ = OQUOTE; |
|
318 |
250594 |
ignore_backslash_newline++; |
|
319 |
✗✓ | 501188 |
PUSH_STATE(SSQUOTE); |
320 |
250594 |
break; |
|
321 |
case '"': |
||
322 |
2427778 |
*wp++ = OQUOTE; |
|
323 |
✗✓ | 4855556 |
PUSH_STATE(SDQUOTE); |
324 |
2427778 |
break; |
|
325 |
default: |
||
326 |
goto Subst; |
||
327 |
} |
||
328 |
break; |
||
329 |
|||
330 |
Subst: |
||
331 |
✓✓✓✓ |
59016699 |
switch (c) { |
332 |
case '\\': |
||
333 |
✓✗✗✓ |
4843920 |
c = getsc(); |
334 |
✗✗✓✓ ✓ |
1210980 |
switch (c) { |
335 |
case '\\': |
||
336 |
case '$': case '`': |
||
337 |
11159 |
*wp++ = QCHAR, *wp++ = c; |
|
338 |
11159 |
break; |
|
339 |
case '"': |
||
340 |
✓✗ | 41492 |
if ((cf & HEREDOC) == 0) { |
341 |
41492 |
*wp++ = QCHAR, *wp++ = c; |
|
342 |
41492 |
break; |
|
343 |
} |
||
344 |
/* FALLTHROUGH */ |
||
345 |
default: |
||
346 |
✗✓ | 1158329 |
if (cf & UNESCAPE) { |
347 |
*wp++ = QCHAR, *wp++ = c; |
||
348 |
break; |
||
349 |
} |
||
350 |
✗✓ | 1158329 |
Xcheck(ws, wp); |
351 |
✓✗ | 1158329 |
if (c) { /* trailing \ is lost */ |
352 |
1158329 |
*wp++ = CHAR, *wp++ = '\\'; |
|
353 |
1158329 |
*wp++ = CHAR, *wp++ = c; |
|
354 |
1158329 |
} |
|
355 |
break; |
||
356 |
} |
||
357 |
break; |
||
358 |
case '$': |
||
359 |
✓✗✓✓ |
13286168 |
c = getsc(); |
360 |
✓✓ | 3321542 |
if (c == '(') /*)*/ { |
361 |
✓✗✓✓ |
49752 |
c = getsc(); |
362 |
✓✓ | 12438 |
if (c == '(') /*)*/ { |
363 |
✗✓ | 12738 |
PUSH_STATE(SASPAREN); |
364 |
6369 |
statep->ls_sasparen.nparen = 2; |
|
365 |
6369 |
statep->ls_sasparen.start = |
|
366 |
6369 |
Xsavepos(ws, wp); |
|
367 |
6369 |
*wp++ = EXPRSUB; |
|
368 |
6369 |
} else { |
|
369 |
6069 |
ungetsc(c); |
|
370 |
✗✓ | 12138 |
PUSH_STATE(SCSPAREN); |
371 |
6069 |
statep->ls_scsparen.nparen = 1; |
|
372 |
6069 |
statep->ls_scsparen.csstate = 0; |
|
373 |
6069 |
*wp++ = COMSUB; |
|
374 |
} |
||
375 |
✓✓ | 3309104 |
} else if (c == '{') /*}*/ { |
376 |
750274 |
*wp++ = OSUBST; |
|
377 |
750274 |
*wp++ = '{'; /*}*/ |
|
378 |
750274 |
wp = get_brace_var(&ws, wp); |
|
379 |
✓✗✓✗ |
3001096 |
c = getsc(); |
380 |
/* allow :# and :% (ksh88 compat) */ |
||
381 |
✓✓ | 750274 |
if (c == ':') { |
382 |
42536 |
*wp++ = CHAR, *wp++ = c; |
|
383 |
✓✗✓✗ |
170144 |
c = getsc(); |
384 |
42536 |
} |
|
385 |
/* If this is a trim operation, |
||
386 |
* treat (,|,) specially in STBRACE. |
||
387 |
*/ |
||
388 |
✓✓ | 1500548 |
if (c == '#' || c == '%') { |
389 |
750274 |
ungetsc(c); |
|
390 |
✗✓ | 3226 |
PUSH_STATE(STBRACE); |
391 |
1613 |
} else { |
|
392 |
ungetsc(c); |
||
393 |
✓✓ | 1497322 |
if (state == SDQUOTE || |
394 |
748661 |
state == SBRACEQ) |
|
395 |
✗✓ | 753840 |
PUSH_STATE(SBRACEQ); |
396 |
else |
||
397 |
✗✓ | 743482 |
PUSH_STATE(SBRACE); |
398 |
} |
||
399 |
✓✓ | 2558830 |
} else if (ctype(c, C_ALPHA)) { |
400 |
2043568 |
*wp++ = OSUBST; |
|
401 |
2043568 |
*wp++ = 'X'; |
|
402 |
2043568 |
do { |
|
403 |
✓✓ | 7297503 |
Xcheck(ws, wp); |
404 |
7296136 |
*wp++ = c; |
|
405 |
✓✓✓✓ |
29168327 |
c = getsc(); |
406 |
✓✓✓✓ |
9399613 |
} while (ctype(c, C_ALPHA) || digit(c)); |
407 |
2043568 |
*wp++ = '\0'; |
|
408 |
2043568 |
*wp++ = CSUBST; |
|
409 |
2043568 |
*wp++ = 'X'; |
|
410 |
2043568 |
ungetsc(c); |
|
411 |
✓✓✓✓ |
3064783 |
} else if (ctype(c, C_VAR1) || digit(c)) { |
412 |
✗✓ | 515252 |
Xcheck(ws, wp); |
413 |
515252 |
*wp++ = OSUBST; |
|
414 |
515252 |
*wp++ = 'X'; |
|
415 |
515252 |
*wp++ = c; |
|
416 |
515252 |
*wp++ = '\0'; |
|
417 |
515252 |
*wp++ = CSUBST; |
|
418 |
515252 |
*wp++ = 'X'; |
|
419 |
515252 |
} else { |
|
420 |
10 |
*wp++ = CHAR, *wp++ = '$'; |
|
421 |
10 |
ungetsc(c); |
|
422 |
} |
||
423 |
break; |
||
424 |
case '`': |
||
425 |
✗✓ | 80566 |
PUSH_STATE(SBQUOTE); |
426 |
40283 |
*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 |
40283 |
statep->ls_sbquote.indquotes = 0; |
|
432 |
Lex_state *s = statep; |
||
433 |
40283 |
Lex_state *base = state_info.base; |
|
434 |
40283 |
while (1) { |
|
435 |
✓✓ | 239758 |
for (; s != base; s--) { |
436 |
✓✓ | 80626 |
if (s->ls_state == SDQUOTE) { |
437 |
1030 |
statep->ls_sbquote.indquotes = 1; |
|
438 |
1030 |
break; |
|
439 |
} |
||
440 |
} |
||
441 |
✓✓ | 40283 |
if (s != base) |
442 |
break; |
||
443 |
✗✓ | 39253 |
if (!(s = s->ls_info.base)) |
444 |
break; |
||
445 |
base = s-- - STATE_BSIZE; |
||
446 |
} |
||
447 |
break; |
||
448 |
default: |
||
449 |
54443894 |
*wp++ = CHAR, *wp++ = c; |
|
450 |
54443894 |
} |
|
451 |
break; |
||
452 |
|||
453 |
case SSQUOTE: |
||
454 |
✓✓ | 2848463 |
if (c == '\'') { |
455 |
✗✓ | 501324 |
POP_STATE(); |
456 |
✗✓ | 250662 |
if (state == SBRACEQ) { |
457 |
*wp++ = CHAR, *wp++ = c; |
||
458 |
break; |
||
459 |
} |
||
460 |
250662 |
*wp++ = CQUOTE; |
|
461 |
250662 |
ignore_backslash_newline--; |
|
462 |
250662 |
} else |
|
463 |
2597801 |
*wp++ = QCHAR, *wp++ = c; |
|
464 |
break; |
||
465 |
|||
466 |
case SDQUOTE: |
||
467 |
✓✓ | 14603001 |
if (c == '"') { |
468 |
✗✓ | 4855556 |
POP_STATE(); |
469 |
2427778 |
*wp++ = CQUOTE; |
|
470 |
} else |
||
471 |
goto Subst; |
||
472 |
2427778 |
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 |
✓✗✓✓ ✓✓ |
211330 |
switch (statep->ls_scsparen.csstate) { |
481 |
case 0: /* normal */ |
||
482 |
✗✓✓✓ ✓✓ |
148965 |
switch (c) { |
483 |
case '(': |
||
484 |
statep->ls_scsparen.nparen++; |
||
485 |
break; |
||
486 |
case ')': |
||
487 |
6069 |
statep->ls_scsparen.nparen--; |
|
488 |
6069 |
break; |
|
489 |
case '\\': |
||
490 |
12 |
statep->ls_scsparen.csstate = 1; |
|
491 |
12 |
break; |
|
492 |
case '"': |
||
493 |
5650 |
statep->ls_scsparen.csstate = 2; |
|
494 |
5650 |
break; |
|
495 |
case '\'': |
||
496 |
830 |
statep->ls_scsparen.csstate = 4; |
|
497 |
830 |
ignore_backslash_newline++; |
|
498 |
830 |
break; |
|
499 |
} |
||
500 |
break; |
||
501 |
|||
502 |
case 1: /* backslash in normal mode */ |
||
503 |
case 3: /* backslash in double quotes */ |
||
504 |
12 |
--statep->ls_scsparen.csstate; |
|
505 |
12 |
break; |
|
506 |
|||
507 |
case 2: /* double quotes */ |
||
508 |
✓✓ | 27675 |
if (c == '"') |
509 |
5650 |
statep->ls_scsparen.csstate = 0; |
|
510 |
✗✓ | 22025 |
else if (c == '\\') |
511 |
statep->ls_scsparen.csstate = 3; |
||
512 |
break; |
||
513 |
|||
514 |
case 4: /* single quotes */ |
||
515 |
✓✓ | 9776 |
if (c == '\'') { |
516 |
830 |
statep->ls_scsparen.csstate = 0; |
|
517 |
830 |
ignore_backslash_newline--; |
|
518 |
830 |
} |
|
519 |
break; |
||
520 |
} |
||
521 |
✓✓ | 173867 |
if (statep->ls_scsparen.nparen == 0) { |
522 |
✗✓ | 12138 |
POP_STATE(); |
523 |
6069 |
*wp++ = 0; /* end of COMSUB */ |
|
524 |
6069 |
} else |
|
525 |
167798 |
*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 |
✓✓ | 62403 |
if (c == '(') |
533 |
36 |
statep->ls_sasparen.nparen++; |
|
534 |
✓✓ | 62367 |
else if (c == ')') { |
535 |
6405 |
statep->ls_sasparen.nparen--; |
|
536 |
✓✓ | 6405 |
if (statep->ls_sasparen.nparen == 1) { |
537 |
/*(*/ |
||
538 |
✓✗✓✓ ✓✗ |
25476 |
if ((c2 = getsc()) == ')') { |
539 |
✗✓ | 12738 |
POP_STATE(); |
540 |
6369 |
*wp++ = 0; /* end of EXPRSUB */ |
|
541 |
6369 |
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 |
56034 |
*wp++ = c; |
|
564 |
56034 |
break; |
|
565 |
|||
566 |
case SBRACEQ: |
||
567 |
/*{*/ |
||
568 |
✓✓ | 1218540 |
if (c == '}') { |
569 |
✗✓ | 753840 |
POP_STATE(); |
570 |
376920 |
*wp++ = CSUBST; |
|
571 |
376920 |
*wp++ = /*{*/ '}'; |
|
572 |
} else |
||
573 |
goto Sbase2; |
||
574 |
376920 |
break; |
|
575 |
|||
576 |
case SBRACE: |
||
577 |
/*{*/ |
||
578 |
✓✓ | 1812490 |
if (c == '}') { |
579 |
✗✓ | 743482 |
POP_STATE(); |
580 |
371741 |
*wp++ = CSUBST; |
|
581 |
371741 |
*wp++ = /*{*/ '}'; |
|
582 |
} else |
||
583 |
goto Sbase1; |
||
584 |
371741 |
break; |
|
585 |
|||
586 |
case STBRACE: |
||
587 |
/* Same as SBRACE, except (,|,) treated specially */ |
||
588 |
/*{*/ |
||
589 |
✓✓ | 5600 |
if (c == '}') { |
590 |
✗✓ | 3226 |
POP_STATE(); |
591 |
1613 |
*wp++ = CSUBST; |
|
592 |
1613 |
*wp++ = /*{*/ '}'; |
|
593 |
✓✓ | 5600 |
} else if (c == '|') { |
594 |
24 |
*wp++ = SPAT; |
|
595 |
✓✓ | 3987 |
} else if (c == '(') { |
596 |
6 |
*wp++ = OPAT; |
|
597 |
6 |
*wp++ = ' '; /* simile for @ */ |
|
598 |
✗✓ | 12 |
PUSH_STATE(SPATTERN); |
599 |
} else |
||
600 |
goto Sbase1; |
||
601 |
break; |
||
602 |
|||
603 |
case SBQUOTE: |
||
604 |
✓✓ | 1432856 |
if (c == '`') { |
605 |
40283 |
*wp++ = 0; |
|
606 |
✗✓ | 80566 |
POP_STATE(); |
607 |
✓✓ | 1432856 |
} else if (c == '\\') { |
608 |
✓✗✗✓ ✗✗✓✓ ✓ |
1880 |
switch (c = getsc()) { |
609 |
case '\\': |
||
610 |
case '$': case '`': |
||
611 |
66 |
*wp++ = c; |
|
612 |
66 |
break; |
|
613 |
case '"': |
||
614 |
✓✗ | 36 |
if (statep->ls_sbquote.indquotes) { |
615 |
36 |
*wp++ = c; |
|
616 |
36 |
break; |
|
617 |
} |
||
618 |
/* FALLTHROUGH */ |
||
619 |
default: |
||
620 |
✓✗ | 368 |
if (c) { /* trailing \ is lost */ |
621 |
368 |
*wp++ = '\\'; |
|
622 |
368 |
*wp++ = c; |
|
623 |
368 |
} |
|
624 |
break; |
||
625 |
} |
||
626 |
} else |
||
627 |
1392103 |
*wp++ = c; |
|
628 |
break; |
||
629 |
|||
630 |
case SWORD: /* ONEWORD */ |
||
631 |
goto Subst; |
||
632 |
|||
633 |
case SLETPAREN: /* LETEXPR: (( ... )) */ |
||
634 |
/*(*/ |
||
635 |
✓✓ | 60 |
if (c == ')') { |
636 |
✗✓ | 12 |
if (statep->ls_sletparen.nparen > 0) |
637 |
--statep->ls_sletparen.nparen; |
||
638 |
/*(*/ |
||
639 |
✓✗✓✓ ✓✗ |
48 |
else if ((c2 = getsc()) == ')') { |
640 |
c = 0; |
||
641 |
12 |
*wp++ = CQUOTE; |
|
642 |
12 |
goto Done; |
|
643 |
} else |
||
644 |
ungetsc(c2); |
||
645 |
✓✗ | 48 |
} 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 |
✓✓ | 5004 |
if (c == '\\') { |
663 |
✓✗✗✓ |
32 |
c = getsc(); |
664 |
✓✗ | 8 |
if (c) { /* trailing \ is lost */ |
665 |
8 |
*wp++ = QCHAR; |
|
666 |
8 |
*wp++ = c; |
|
667 |
8 |
} |
|
668 |
✓✓ | 4996 |
} else if (c == '\'') { |
669 |
✗✓ | 136 |
PUSH_STATE(SSQUOTE); |
670 |
68 |
*wp++ = OQUOTE; |
|
671 |
68 |
ignore_backslash_newline++; |
|
672 |
✓✓ | 4996 |
} else if (c == '"') { |
673 |
24 |
state = statep->ls_state = SHEREDQUOTE; |
|
674 |
24 |
*wp++ = OQUOTE; |
|
675 |
24 |
} else { |
|
676 |
4904 |
*wp++ = CHAR; |
|
677 |
4904 |
*wp++ = c; |
|
678 |
} |
||
679 |
break; |
||
680 |
|||
681 |
case SHEREDQUOTE: /* " in <<,<<- delimiter */ |
||
682 |
✓✓ | 96 |
if (c == '"') { |
683 |
24 |
*wp++ = CQUOTE; |
|
684 |
24 |
state = statep->ls_state = SHEREDELIM; |
|
685 |
24 |
} else { |
|
686 |
✓✓ | 72 |
if (c == '\\') { |
687 |
✓✗✗✓ ✗✗✗✗ ✓ |
24 |
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 |
72 |
*wp++ = CHAR; |
|
700 |
72 |
*wp++ = c; |
|
701 |
} |
||
702 |
break; |
||
703 |
|||
704 |
case SPATTERN: /* in *(...|...) pattern (*+?@!) */ |
||
705 |
✓✓ | 4548 |
if ( /*(*/ c == ')') { |
706 |
504 |
*wp++ = CPAT; |
|
707 |
✗✓ | 1008 |
POP_STATE(); |
708 |
✓✓ | 4548 |
} else if (c == '|') { |
709 |
312 |
*wp++ = SPAT; |
|
710 |
✓✓ | 4044 |
} else if (c == '(') { |
711 |
24 |
*wp++ = OPAT; |
|
712 |
24 |
*wp++ = ' '; /* simile for @ */ |
|
713 |
✗✓ | 48 |
PUSH_STATE(SPATTERN); |
714 |
} else |
||
715 |
goto Sbase1; |
||
716 |
break; |
||
717 |
} |
||
718 |
} |
||
719 |
Done: |
||
720 |
✓✓ | 18891999 |
Xcheck(ws, wp); |
721 |
✗✓ | 18881363 |
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 |
✓✓ | 18881363 |
if (state == SHEREDELIM) |
727 |
2278 |
state = SBASE; |
|
728 |
|||
729 |
18881363 |
dp = Xstring(ws, wp); |
|
730 |
✓✓✓✓ |
19072688 |
if ((c == '<' || c == '>') && state == SBASE && |
731 |
✓✓ | 289904 |
((c2 = Xlength(ws, wp)) == 0 || |
732 |
✓✓✓✗ |
573992 |
(c2 == 2 && dp[0] == CHAR && digit(dp[1])))) { |
733 |
289879 |
struct ioword *iop = alloc(sizeof(*iop), ATEMP); |
|
734 |
|||
735 |
✓✓ | 289879 |
if (c2 == 2) |
736 |
191317 |
iop->unit = dp[1] - '0'; |
|
737 |
else |
||
738 |
98562 |
iop->unit = c == '>'; /* 0 for <, 1 for > */ |
|
739 |
|||
740 |
✓✗✓✓ |
1159516 |
c2 = getsc(); |
741 |
/* <<, >>, <> are ok, >< is not */ |
||
742 |
✓✓✗✓ |
574362 |
if (c == c2 || (c == '<' && c2 == '>')) { |
743 |
✓✗ | 16188 |
iop->flag = c == c2 ? |
744 |
5396 |
(c == '>' ? IOCAT : IOHERE) : IORDWR; |
|
745 |
✓✓ | 5396 |
if (iop->flag == IOHERE) { |
746 |
✓✗✓✓ ✓✓ |
9112 |
if ((c2 = getsc()) == '-') |
747 |
108 |
iop->flag |= IOSKIP; |
|
748 |
else |
||
749 |
2170 |
ungetsc(c2); |
|
750 |
} |
||
751 |
✓✓ | 284483 |
} else if (c2 == '&') |
752 |
187879 |
iop->flag = IODUP | (c == '<' ? IORDUP : 0); |
|
753 |
else { |
||
754 |
96604 |
iop->flag = c == '>' ? IOWRITE : IOREAD; |
|
755 |
✗✓ | 96604 |
if (c == '>' && c2 == '|') |
756 |
iop->flag |= IOCLOB; |
||
757 |
else |
||
758 |
96604 |
ungetsc(c2); |
|
759 |
} |
||
760 |
|||
761 |
289879 |
iop->name = NULL; |
|
762 |
289879 |
iop->delim = NULL; |
|
763 |
289879 |
iop->heredoc = NULL; |
|
764 |
289879 |
Xfree(ws, wp); /* free word */ |
|
765 |
289879 |
yylval.iop = iop; |
|
766 |
return REDIR; |
||
767 |
} |
||
768 |
|||
769 |
✓✓ | 18591484 |
if (wp == dp && state == SBASE) { |
770 |
5386356 |
Xfree(ws, wp); /* free word */ |
|
771 |
/* no word, process LEX1 character */ |
||
772 |
✓✗✗✓ ✓✓✓ |
5386356 |
switch (c) { |
773 |
default: |
||
774 |
1648245 |
return c; |
|
775 |
|||
776 |
case '|': |
||
777 |
case '&': |
||
778 |
case ';': |
||
779 |
✓✓✓✓ ✓✓ |
8949068 |
if ((c2 = getsc()) == c) |
780 |
✓✓ | 137896 |
c = (c == ';') ? BREAK : |
781 |
✓✓ | 126393 |
(c == '|') ? LOGOR : |
782 |
60166 |
(c == '&') ? LOGAND : |
|
783 |
YYERRCODE; |
||
784 |
✗✓ | 2168327 |
else if (c == '|' && c2 == '&') |
785 |
c = COPROC; |
||
786 |
else |
||
787 |
2168327 |
ungetsc(c2); |
|
788 |
2237275 |
return c; |
|
789 |
|||
790 |
case '\n': |
||
791 |
1436601 |
gethere(); |
|
792 |
✓✓ | 1436601 |
if (cf & CONTIN) |
793 |
24423 |
goto Again; |
|
794 |
1412178 |
return c; |
|
795 |
|||
796 |
case '(': /*)*/ |
||
797 |
✓✓ | 30721 |
if (!Flag(FSH)) { |
798 |
✓✗✓✓ ✓✓ |
4940 |
if ((c2 = getsc()) == '(') /*)*/ |
799 |
/* XXX need to handle ((...); (...)) */ |
||
800 |
12 |
c = MDPAREN; |
|
801 |
else |
||
802 |
1223 |
ungetsc(c2); |
|
803 |
} |
||
804 |
30721 |
return c; |
|
805 |
/*(*/ |
||
806 |
case ')': |
||
807 |
33508 |
return c; |
|
808 |
} |
||
809 |
} |
||
810 |
|||
811 |
13205128 |
*wp++ = EOS; /* terminate word */ |
|
812 |
13205128 |
yylval.cp = Xclose(ws, wp); |
|
813 |
✓✓ | 13205128 |
if (state == SWORD || state == SLETPAREN) /* ONEWORD? */ |
814 |
262504 |
return LWORD; |
|
815 |
12942624 |
ungetsc(c); /* unget terminator */ |
|
816 |
|||
817 |
/* copy word to unprefixed string ident */ |
||
818 |
✓✓✓✓ |
164003339 |
for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; ) |
819 |
41725292 |
*dp++ = *sp++; |
|
820 |
/* Make sure the ident array stays '\0' padded */ |
||
821 |
12942624 |
memset(dp, 0, (ident+IDENT) - dp + 1); |
|
822 |
✓✓ | 12942624 |
if (c != EOS) |
823 |
3782683 |
*ident = '\0'; /* word is not unquoted */ |
|
824 |
|||
825 |
✓✓✓✓ |
22102565 |
if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) { |
826 |
struct tbl *p; |
||
827 |
4520803 |
int h = hash(ident); |
|
828 |
|||
829 |
/* { */ |
||
830 |
✓✓✓✓ ✗✗ |
8958040 |
if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) && |
831 |
✓✓✗✓ |
279860 |
(!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}')) { |
832 |
278999 |
afree(yylval.cp, ATEMP); |
|
833 |
278999 |
return p->val.i; |
|
834 |
} |
||
835 |
✓✓✓✓ ✓✗ |
8481775 |
if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) && |
836 |
912 |
(p->flag & ISSET)) { |
|
837 |
Source *s; |
||
838 |
|||
839 |
✓✓ | 1944 |
for (s = source; s->type == SALIAS; s = s->next) |
840 |
✓✓ | 260 |
if (s->u.tblp == p) |
841 |
200 |
return LWORD; |
|
842 |
/* push alias expansion */ |
||
843 |
712 |
s = pushs(SALIAS, source->areap); |
|
844 |
712 |
s->start = s->str = p->val.s; |
|
845 |
712 |
s->u.tblp = p; |
|
846 |
712 |
s->next = source; |
|
847 |
712 |
source = s; |
|
848 |
712 |
afree(yylval.cp, ATEMP); |
|
849 |
712 |
goto Again; |
|
850 |
} |
||
851 |
✓✓✓ | 4240892 |
} |
852 |
|||
853 |
12662713 |
return LWORD; |
|
854 |
18856222 |
} |
|
855 |
|||
856 |
static void |
||
857 |
gethere(void) |
||
858 |
{ |
||
859 |
struct ioword **p; |
||
860 |
|||
861 |
✓✓ | 4314371 |
for (p = heres; p < herep; p++) |
862 |
2278 |
readhere(*p); |
|
863 |
1436601 |
herep = heres; |
|
864 |
1436601 |
} |
|
865 |
|||
866 |
/* |
||
867 |
* read "<<word" text into temp file |
||
868 |
*/ |
||
869 |
|||
870 |
static void |
||
871 |
readhere(struct ioword *iop) |
||
872 |
{ |
||
873 |
int c; |
||
874 |
4556 |
char *volatile eof; |
|
875 |
char *eofp; |
||
876 |
int skiptabs; |
||
877 |
2278 |
XString xs; |
|
878 |
char *xp; |
||
879 |
int xpos; |
||
880 |
|||
881 |
2278 |
eof = evalstr(iop->delim, 0); |
|
882 |
|||
883 |
✓✓ | 2278 |
if (!(iop->flag & IOEVAL)) |
884 |
100 |
ignore_backslash_newline++; |
|
885 |
|||
886 |
2278 |
Xinit(xs, xp, 256, ATEMP); |
|
887 |
|||
888 |
14984 |
for (;;) { |
|
889 |
14984 |
eofp = eof; |
|
890 |
14984 |
skiptabs = iop->flag & IOSKIP; |
|
891 |
14984 |
xpos = Xsavepos(xs, xp); |
|
892 |
✓✓✓✓ ✓✓ |
87894 |
while ((c = getsc()) != 0) { |
893 |
✓✓ | 20571 |
if (skiptabs) { |
894 |
✓✓ | 738 |
if (c == '\t') |
895 |
438 |
continue; |
|
896 |
skiptabs = 0; |
||
897 |
300 |
} |
|
898 |
✓✓ | 20133 |
if (c != *eofp) |
899 |
break; |
||
900 |
✗✓ | 5166 |
Xcheck(xs, xp); |
901 |
5166 |
Xput(xs, xp, c); |
|
902 |
5166 |
eofp++; |
|
903 |
} |
||
904 |
/* Allow EOF here so commands with out trailing newlines |
||
905 |
* will work (eg, ksh -c '...', $(...), etc). |
||
906 |
*/ |
||
907 |
✓✓✓✗ |
17256 |
if (*eofp == '\0' && (c == 0 || c == '\n')) { |
908 |
2272 |
xp = Xrestpos(xs, xp, xpos); |
|
909 |
break; |
||
910 |
} |
||
911 |
12712 |
ungetsc(c); |
|
912 |
✓✓✓✓ ✓✓ |
945058 |
while ((c = getsc()) != '\n') { |
913 |
✓✓ | 176308 |
if (c == 0) |
914 |
yyerror("here document `%s' unclosed\n", eof); |
||
915 |
✓✓ | 176545 |
Xcheck(xs, xp); |
916 |
176302 |
Xput(xs, xp, c); |
|
917 |
} |
||
918 |
✓✓ | 12716 |
Xcheck(xs, xp); |
919 |
12706 |
Xput(xs, xp, c); |
|
920 |
} |
||
921 |
2272 |
Xput(xs, xp, '\0'); |
|
922 |
2272 |
iop->heredoc = Xclose(xs, xp); |
|
923 |
|||
924 |
✓✓ | 2272 |
if (!(iop->flag & IOEVAL)) |
925 |
100 |
ignore_backslash_newline--; |
|
926 |
2272 |
} |
|
927 |
|||
928 |
void |
||
929 |
yyerror(const char *fmt, ...) |
||
930 |
{ |
||
931 |
24 |
va_list va; |
|
932 |
|||
933 |
/* pop aliases and re-reads */ |
||
934 |
✓✗✗✓ |
48 |
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 |
3716076 |
s = alloc(sizeof(Source), areap); |
|
955 |
1858038 |
s->type = type; |
|
956 |
1858038 |
s->str = null; |
|
957 |
1858038 |
s->start = NULL; |
|
958 |
1858038 |
s->line = 0; |
|
959 |
1858038 |
s->cmd_offset = 0; |
|
960 |
1858038 |
s->errline = 0; |
|
961 |
1858038 |
s->file = NULL; |
|
962 |
1858038 |
s->flags = 0; |
|
963 |
1858038 |
s->next = NULL; |
|
964 |
1858038 |
s->areap = areap; |
|
965 |
✓✓ | 1858038 |
if (type == SFILE || type == SSTDIN) { |
966 |
char *dummy; |
||
967 |
16530 |
Xinit(s->xs, dummy, 256, s->areap); |
|
968 |
16530 |
} else |
|
969 |
1841508 |
memset(&s->xs, 0, sizeof(s->xs)); |
|
970 |
1858038 |
return s; |
|
971 |
} |
||
972 |
|||
973 |
static int |
||
974 |
getsc__(void) |
||
975 |
{ |
||
976 |
13760476 |
Source *s = source; |
|
977 |
int c; |
||
978 |
|||
979 |
✓✓ | 17450992 |
while ((c = *s->str++) == 0) { |
980 |
8727744 |
s->str = NULL; /* return 0 for EOF by default */ |
|
981 |
✓✗✓✓ ✓✓✗✓ |
8727744 |
switch (s->type) { |
982 |
case SEOF: |
||
983 |
3308596 |
s->str = null; |
|
984 |
3308596 |
return 0; |
|
985 |
|||
986 |
case SSTDIN: |
||
987 |
case SFILE: |
||
988 |
1521403 |
getsc_line(s); |
|
989 |
1521403 |
break; |
|
990 |
|||
991 |
case SWSTR: |
||
992 |
break; |
||
993 |
|||
994 |
case SSTRING: |
||
995 |
break; |
||
996 |
|||
997 |
case SWORDS: |
||
998 |
163629 |
s->start = s->str = *s->u.strv++; |
|
999 |
163629 |
s->type = SWORDSEP; |
|
1000 |
163629 |
break; |
|
1001 |
|||
1002 |
case SWORDSEP: |
||
1003 |
✓✓ | 163539 |
if (*s->u.strv == NULL) { |
1004 |
119131 |
s->start = s->str = "\n"; |
|
1005 |
s->type = SEOF; |
||
1006 |
119131 |
} else { |
|
1007 |
44408 |
s->start = s->str = " "; |
|
1008 |
s->type = SWORDS; |
||
1009 |
} |
||
1010 |
163539 |
break; |
|
1011 |
|||
1012 |
case SALIAS: |
||
1013 |
✓✓ | 1074 |
if (s->flags & SF_ALIASEND) { |
1014 |
/* pass on an unused SF_ALIAS flag */ |
||
1015 |
362 |
source = s->next; |
|
1016 |
362 |
source->flags |= s->flags & SF_ALIAS; |
|
1017 |
362 |
s = source; |
|
1018 |
✓✗✓✓ |
1786 |
} else if (*s->u.tblp->val.s && |
1019 |
712 |
isspace((unsigned char)strchr(s->u.tblp->val.s, 0)[-1])) { |
|
1020 |
260 |
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 |
260 |
s->flags |= SF_ALIAS; |
|
1026 |
260 |
} 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 |
452 |
source = s->next; /* pop source stack */ |
|
1037 |
452 |
source->flags |= s->flags & SF_ALIAS; |
|
1038 |
452 |
c = getsc__(); |
|
1039 |
✓✓ | 452 |
if (c) { |
1040 |
440 |
s->flags |= SF_ALIASEND; |
|
1041 |
440 |
s->ugbuf[0] = c; s->ugbuf[1] = '\0'; |
|
1042 |
440 |
s->start = s->str = s->ugbuf; |
|
1043 |
440 |
s->next = source; |
|
1044 |
440 |
source = s; |
|
1045 |
} else { |
||
1046 |
12 |
s = source; |
|
1047 |
/* avoid reading eof twice */ |
||
1048 |
12 |
s->str = NULL; |
|
1049 |
12 |
break; |
|
1050 |
} |
||
1051 |
} |
||
1052 |
1062 |
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 |
✓✓ | 3569503 |
if (s->str == NULL) { |
1061 |
1724776 |
s->type = SEOF; |
|
1062 |
1724776 |
s->start = s->str = null; |
|
1063 |
1724776 |
return '\0'; |
|
1064 |
} |
||
1065 |
✓✓ | 1844727 |
if (s->flags & SF_ECHO) { |
1066 |
6 |
shf_puts(s->str, shl_out); |
|
1067 |
6 |
shf_flush(shl_out); |
|
1068 |
6 |
} |
|
1069 |
} |
||
1070 |
1846211 |
return c; |
|
1071 |
6879583 |
} |
|
1072 |
|||
1073 |
static void |
||
1074 |
getsc_line(Source *s) |
||
1075 |
{ |
||
1076 |
3042806 |
char *xp = Xstring(s->xs, xp); |
|
1077 |
✓✓ | 3045002 |
int interactive = Flag(FTALKING) && s->type == SSTDIN; |
1078 |
✓✓ | 3044482 |
int have_tty = interactive && (s->flags & SF_TTY); |
1079 |
|||
1080 |
/* Done here to ensure nothing odd happens when a timeout occurs */ |
||
1081 |
✓✓ | 1537933 |
XcheckN(s->xs, xp, LINE); |
1082 |
1521403 |
*xp = '\0'; |
|
1083 |
1521403 |
s->start = s->str = xp; |
|
1084 |
|||
1085 |
✗✓ | 1521403 |
if (have_tty && ksh_tmout) { |
1086 |
ksh_tmout_state = TMOUT_READING; |
||
1087 |
alarm(ksh_tmout); |
||
1088 |
} |
||
1089 |
#ifdef EDIT |
||
1090 |
✓✓✗✗ |
1521403 |
if (have_tty && (0 |
1091 |
# ifdef VI |
||
1092 |
692 |
|| Flag(FVI) |
|
1093 |
# endif /* VI */ |
||
1094 |
# ifdef EMACS |
||
1095 |
✓✓✗✓ |
958 |
|| Flag(FEMACS) || Flag(FGMACS) |
1096 |
# endif /* EMACS */ |
||
1097 |
)) { |
||
1098 |
int nread; |
||
1099 |
|||
1100 |
692 |
nread = x_read(xp, LINE); |
|
1101 |
692 |
if (nread < 0) /* read error */ |
|
1102 |
nread = 0; |
||
1103 |
692 |
xp[nread] = '\0'; |
|
1104 |
xp += nread; |
||
1105 |
692 |
} |
|
1106 |
else |
||
1107 |
#endif /* EDIT */ |
||
1108 |
{ |
||
1109 |
✓✓ | 1520711 |
if (interactive) { |
1110 |
984 |
pprompt(prompt, 0); |
|
1111 |
984 |
} else |
|
1112 |
1519727 |
s->line++; |
|
1113 |
|||
1114 |
1520735 |
while (1) { |
|
1115 |
1520735 |
char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf); |
|
1116 |
|||
1117 |
✓✓✗✓ ✗✗ |
1523866 |
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 |
✓✓✓✓ |
3038339 |
if (!p || (xp = p, xp[-1] == '\n')) |
1125 |
1520711 |
break; |
|
1126 |
/* double buffer size */ |
||
1127 |
24 |
xp++; /* move past null so doubling works... */ |
|
1128 |
✓✓ | 30 |
XcheckN(s->xs, xp, Xlength(s->xs, xp)); |
1129 |
24 |
xp--; /* ...and move back again */ |
|
1130 |
✓✓✓✗ |
24 |
} |
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 |
✓✓ | 1520711 |
if (s->type == SSTDIN) |
1138 |
20474 |
shf_flush(s->u.shf); |
|
1139 |
} |
||
1140 |
/* XXX: temporary kludge to restore source after a |
||
1141 |
* trap may have been executed. |
||
1142 |
*/ |
||
1143 |
1520748 |
source = s; |
|
1144 |
✗✓ | 1520748 |
if (have_tty && ksh_tmout) { |
1145 |
ksh_tmout_state = TMOUT_EXECUTING; |
||
1146 |
alarm(0); |
||
1147 |
} |
||
1148 |
1520748 |
s->start = s->str = Xstring(s->xs, xp); |
|
1149 |
1520748 |
strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp)); |
|
1150 |
/* Note: if input is all nulls, this is not eof */ |
||
1151 |
✓✓ | 1520748 |
if (Xlength(s->xs, xp) == 0) { /* EOF */ |
1152 |
✓✓ | 3117 |
if (s->type == SFILE) |
1153 |
1877 |
shf_fdclose(s->u.shf); |
|
1154 |
3117 |
s->str = NULL; |
|
1155 |
✓✓ | 1520748 |
} else if (interactive) { |
1156 |
#ifdef HISTORY |
||
1157 |
char *p = Xstring(s->xs, xp); |
||
1158 |
✓✓ | 825 |
if (cur_prompt == PS1) |
1159 |
✓✓✓✓ ✓✗ |
2514 |
while (*p && ctype(*p, C_IFS) && ctype(*p, C_IFSWS)) |
1160 |
21 |
p++; |
|
1161 |
✓✓ | 825 |
if (*p) { |
1162 |
816 |
s->line++; |
|
1163 |
816 |
histsave(s->line, s->str, 1); |
|
1164 |
816 |
} |
|
1165 |
#endif /* HISTORY */ |
||
1166 |
825 |
} |
|
1167 |
✓✓ | 1520748 |
if (interactive) |
1168 |
1021 |
set_prompt(PS2, NULL); |
|
1169 |
1520748 |
} |
|
1170 |
|||
1171 |
static char * |
||
1172 |
special_prompt_expand(char *str) |
||
1173 |
{ |
||
1174 |
char *p = str; |
||
1175 |
|||
1176 |
✗✓ | 4992 |
while ((p = strstr(p, "\\$")) != NULL) { |
1177 |
*(p+1) = 'p'; |
||
1178 |
} |
||
1179 |
1664 |
return str; |
|
1180 |
} |
||
1181 |
|||
1182 |
void |
||
1183 |
set_prompt(int to, Source *s) |
||
1184 |
{ |
||
1185 |
char *ps1; |
||
1186 |
Area *saved_atemp; |
||
1187 |
|||
1188 |
8055 |
cur_prompt = to; |
|
1189 |
|||
1190 |
✓✓✓ | 5370 |
switch (to) { |
1191 |
case PS1: /* command */ |
||
1192 |
1664 |
ps1 = str_save(str_val(global("PS1")), ATEMP); |
|
1193 |
1664 |
saved_atemp = ATEMP; /* ps1 is freed by substitute() */ |
|
1194 |
1664 |
newenv(E_ERRH); |
|
1195 |
✗✓ | 1664 |
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 |
1664 |
char *tmp = special_prompt_expand(ps1); |
|
1206 |
1664 |
prompt = str_save(substitute(tmp, 0), saved_atemp); |
|
1207 |
} |
||
1208 |
1664 |
quitenv(NULL); |
|
1209 |
1664 |
break; |
|
1210 |
case PS2: /* command continuation */ |
||
1211 |
1021 |
prompt = str_val(global("PS2")); |
|
1212 |
1021 |
break; |
|
1213 |
} |
||
1214 |
2685 |
} |
|
1215 |
|||
1216 |
static int |
||
1217 |
dopprompt(const char *sp, int ntruncate, const char **spp, int doprint) |
||
1218 |
{ |
||
1219 |
4896 |
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 |
2448 |
time_t t; |
|
1224 |
|||
1225 |
✓✓✗✓ |
4854 |
if (*cp && cp[1] == '\r') { |
1226 |
delimiter = *cp; |
||
1227 |
cp += 2; |
||
1228 |
} |
||
1229 |
|||
1230 |
✓✓ | 15582 |
while (*cp != 0) { |
1231 |
delimitthis = 0; |
||
1232 |
✗✓✗✗ |
5358 |
if (indelimit && *cp != delimiter) |
1233 |
; |
||
1234 |
✓✗✗✓ |
10716 |
else if (*cp == '\n' || *cp == '\r') { |
1235 |
totlen = 0; |
||
1236 |
sp = cp + 1; |
||
1237 |
✗✓ | 5358 |
} else if (*cp == '\t') { |
1238 |
if (counting) |
||
1239 |
totlen = (totlen | 7) + 1; |
||
1240 |
✗✓ | 5358 |
} else if (*cp == delimiter) { |
1241 |
indelimit = !indelimit; |
||
1242 |
delimitthis = 1; |
||
1243 |
} |
||
1244 |
|||
1245 |
✓✓ | 5358 |
if (*cp == '\\') { |
1246 |
30 |
cp++; |
|
1247 |
✓✗ | 30 |
if (!*cp) |
1248 |
break; |
||
1249 |
✗✓ | 30 |
if (Flag(FSH)) |
1250 |
snprintf(strbuf, sizeof strbuf, "\\%c", *cp); |
||
1251 |
✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✓✗ ✗✗✗ |
30 |
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 |
✓✗✓✗ ✗✓ |
90 |
if ((cp[1] > '7' || cp[1] < '0') || |
1398 |
✓✗ | 60 |
(cp[2] > '7' || cp[2] < '0')) { |
1399 |
snprintf(strbuf, sizeof strbuf, |
||
1400 |
"\\%c", *cp); |
||
1401 |
break; |
||
1402 |
} |
||
1403 |
60 |
n = (cp[0] - '0') * 8 * 8 + (cp[1] - '0') * 8 + |
|
1404 |
30 |
(cp[2] - '0'); |
|
1405 |
30 |
snprintf(strbuf, sizeof strbuf, "%c", n); |
|
1406 |
cp += 2; |
||
1407 |
30 |
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 |
30 |
cp++; |
|
1426 |
|||
1427 |
30 |
str = strbuf; |
|
1428 |
30 |
len = strlen(str); |
|
1429 |
✗✓ | 30 |
if (ntruncate) { |
1430 |
if (ntruncate >= len) { |
||
1431 |
ntruncate -= len; |
||
1432 |
continue; |
||
1433 |
} |
||
1434 |
str += ntruncate; |
||
1435 |
len -= ntruncate; |
||
1436 |
ntruncate = 0; |
||
1437 |
} |
||
1438 |
✓✗ | 30 |
if (doprint) |
1439 |
30 |
shf_write(str, len, shl_out); |
|
1440 |
✓✗ | 30 |
if (counting && !indelimit && !delimitthis) |
1441 |
30 |
totlen += len; |
|
1442 |
30 |
continue; |
|
1443 |
✓✗ | 5328 |
} else if (*cp != '!') |
1444 |
5328 |
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 |
✗✓ | 5328 |
if (counting && ntruncate) |
1469 |
--ntruncate; |
||
1470 |
✓✓ | 5328 |
else if (doprint) { |
1471 |
✓✓ | 6480 |
shf_putc(c, shl_out); |
1472 |
} |
||
1473 |
✓✗ | 5328 |
if (counting && !indelimit && !delimitthis) |
1474 |
5328 |
totlen++; |
|
1475 |
} |
||
1476 |
✓✓ | 2448 |
if (doprint) |
1477 |
1743 |
shf_flush(shl_out); |
|
1478 |
✓✓ | 2448 |
if (spp) |
1479 |
692 |
*spp = sp; |
|
1480 |
2448 |
return (totlen); |
|
1481 |
2448 |
} |
|
1482 |
|||
1483 |
void |
||
1484 |
pprompt(const char *cp, int ntruncate) |
||
1485 |
{ |
||
1486 |
3486 |
dopprompt(cp, ntruncate, NULL, 1); |
|
1487 |
1743 |
} |
|
1488 |
|||
1489 |
int |
||
1490 |
promptlen(const char *cp, const char **spp) |
||
1491 |
{ |
||
1492 |
1410 |
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 |
5042634 |
while (1) { |
|
1510 |
✓✗✓✓ |
22211978 |
c = getsc(); |
1511 |
/* State machine to figure out where the variable part ends. */ |
||
1512 |
✓✓✓✓ ✓✓ |
9334898 |
switch (state) { |
1513 |
case PS_INITIAL: |
||
1514 |
✓✓ | 750274 |
if (c == '#') { |
1515 |
state = PS_SAW_HASH; |
||
1516 |
96 |
break; |
|
1517 |
} |
||
1518 |
/* FALLTHROUGH */ |
||
1519 |
case PS_SAW_HASH: |
||
1520 |
✓✓ | 750274 |
if (letter(c)) |
1521 |
744989 |
state = PS_IDENT; |
|
1522 |
✓✓ | 5285 |
else if (digit(c)) |
1523 |
5273 |
state = PS_NUMBER; |
|
1524 |
✓✓ | 12 |
else if (ctype(c, C_VAR1)) |
1525 |
6 |
state = PS_VAR1; |
|
1526 |
else |
||
1527 |
state = PS_END; |
||
1528 |
break; |
||
1529 |
case PS_IDENT: |
||
1530 |
✓✓✓✓ |
4596210 |
if (!letnum(c)) { |
1531 |
state = PS_END; |
||
1532 |
✓✓ | 744989 |
if (c == '[') { |
1533 |
554 |
char *tmp, *p; |
|
1534 |
|||
1535 |
✗✓ | 554 |
if (!arraysub(&tmp)) |
1536 |
yyerror("missing ]\n"); |
||
1537 |
554 |
*wp++ = c; |
|
1538 |
✓✓ | 6516 |
for (p = tmp; *p; ) { |
1539 |
✗✓ | 2704 |
Xcheck(*wsp, wp); |
1540 |
2704 |
*wp++ = *p++; |
|
1541 |
} |
||
1542 |
554 |
afree(tmp, ATEMP); |
|
1543 |
✓✗✓✗ |
2216 |
c = getsc(); /* the ] */ |
1544 |
554 |
} |
|
1545 |
} |
||
1546 |
break; |
||
1547 |
case PS_NUMBER: |
||
1548 |
✓✗ | 5273 |
if (!digit(c)) |
1549 |
5273 |
state = PS_END; |
|
1550 |
break; |
||
1551 |
case PS_VAR1: |
||
1552 |
state = PS_END; |
||
1553 |
6 |
break; |
|
1554 |
case PS_END: /* keep gcc happy */ |
||
1555 |
break; |
||
1556 |
} |
||
1557 |
✓✓ | 4292360 |
if (state == PS_END) { |
1558 |
750274 |
*wp++ = '\0'; /* end of variable part */ |
|
1559 |
750274 |
ungetsc(c); |
|
1560 |
break; |
||
1561 |
} |
||
1562 |
✓✓ | 3542943 |
Xcheck(*wsp, wp); |
1563 |
3542086 |
*wp++ = c; |
|
1564 |
} |
||
1565 |
750274 |
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 |
2126 |
XString ws; |
|
1577 |
char *wp; |
||
1578 |
char c; |
||
1579 |
int depth = 1; /* we are just past the initial [ */ |
||
1580 |
|||
1581 |
1063 |
Xinit(ws, wp, 32, ATEMP); |
|
1582 |
|||
1583 |
1063 |
do { |
|
1584 |
✓✗✓✗ |
27608 |
c = getsc(); |
1585 |
✗✓ | 6902 |
Xcheck(ws, wp); |
1586 |
6902 |
*wp++ = c; |
|
1587 |
✓✓ | 6902 |
if (c == '[') |
1588 |
315 |
depth++; |
|
1589 |
✓✓ | 6587 |
else if (c == ']') |
1590 |
1378 |
depth--; |
|
1591 |
✓✓✓✗ ✓✗ |
18580 |
} while (depth > 0 && c && c != '\n'); |
1592 |
|||
1593 |
1063 |
*wp++ = '\0'; |
|
1594 |
1063 |
*strp = Xclose(ws, wp); |
|
1595 |
|||
1596 |
2126 |
return depth == 0 ? 1 : 0; |
|
1597 |
1063 |
} |
|
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 |
✓✓ | 77767072 |
if (backslash_skip) |
1604 |
24186 |
backslash_skip--; |
|
1605 |
/* Don't unget eof... */ |
||
1606 |
✓✓ | 38883536 |
if (source->str == null && c == '\0') |
1607 |
3122582 |
return source->str; |
|
1608 |
✓✗ | 35760954 |
if (source->str > source->start) |
1609 |
35760954 |
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 |
35760954 |
return source->str; |
|
1620 |
38883536 |
} |
|
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 |
✓✓ | 20832626 |
if (ignore_backslash_newline) |
1630 |
✓✓ | 493734 |
return getsc_(); |
1631 |
|||
1632 |
✓✓ | 10251735 |
if (backslash_skip == 1) { |
1633 |
1221165 |
backslash_skip = 2; |
|
1634 |
✓✓ | 3663495 |
return getsc_(); |
1635 |
} |
||
1636 |
|||
1637 |
9030570 |
backslash_skip = 0; |
|
1638 |
|||
1639 |
9030570 |
while (1) { |
|
1640 |
✓✓ | 27348611 |
c = getsc_(); |
1641 |
✓✓ | 9115767 |
if (c == '\\') { |
1642 |
✓✓✓✓ |
3993609 |
if ((c2 = getsc_()) == '\n') |
1643 |
/* ignore the \newline; get the next char... */ |
||
1644 |
85852 |
continue; |
|
1645 |
1245351 |
ungetsc(c2); |
|
1646 |
1245351 |
backslash_skip = 1; |
|
1647 |
1245351 |
} |
|
1648 |
9029915 |
return c; |
|
1649 |
} |
||
1650 |
10415658 |
} |
|
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) |