GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: lex.c,v 1.69 2016/04/27 12:46:23 naddy 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 |
289849 |
{ |
|
138 |
Lex_state states[STATE_BSIZE], *statep; |
||
139 |
State_info state_info; |
||
140 |
int c, state; |
||
141 |
XString ws; /* expandable output word */ |
||
142 |
char *wp; /* output word pointer */ |
||
143 |
char *sp, *dp; |
||
144 |
int c2; |
||
145 |
|||
146 |
|||
147 |
289849 |
Again: |
|
148 |
289849 |
states[0].ls_state = SINVALID; |
|
149 |
289849 |
states[0].ls_info.base = NULL; |
|
150 |
289849 |
statep = &states[1]; |
|
151 |
289849 |
state_info.base = states; |
|
152 |
289849 |
state_info.end = &states[STATE_BSIZE]; |
|
153 |
|||
154 |
289849 |
Xinit(ws, wp, 64, ATEMP); |
|
155 |
|||
156 |
289849 |
backslash_skip = 0; |
|
157 |
289849 |
ignore_backslash_newline = 0; |
|
158 |
|||
159 |
✓✓ | 289849 |
if (cf&ONEWORD) |
160 |
4719 |
state = SWORD; |
|
161 |
✓✓ | 285130 |
else if (cf&LETEXPR) { |
162 |
4 |
*wp++ = OQUOTE; /* enclose arguments in (double) quotes */ |
|
163 |
4 |
state = SLETPAREN; |
|
164 |
4 |
statep->ls_sletparen.nparen = 0; |
|
165 |
} else { /* normal lexing */ |
||
166 |
✓✓ | 285126 |
state = (cf & HEREDELIM) ? SHEREDELIM : SBASE; |
167 |
✓✓✓✗ ✓✓✓✓ |
506617 |
while ((c = getsc()) == ' ' || c == '\t') |
168 |
; |
||
169 |
✓✓ | 285126 |
if (c == '#') { |
170 |
5279 |
ignore_backslash_newline++; |
|
171 |
✓✓✓✗ ✓✗✓✓ |
234855 |
while ((c = getsc()) != '\0' && c != '\n') |
172 |
; |
||
173 |
5279 |
ignore_backslash_newline--; |
|
174 |
} |
||
175 |
285126 |
ungetsc(c); |
|
176 |
} |
||
177 |
✓✓ | 289849 |
if (source->flags & SF_ALIAS) { /* trailing ' ' in alias definition */ |
178 |
28 |
source->flags &= ~SF_ALIAS; |
|
179 |
/* In POSIX mode, a trailing space only counts if we are |
||
180 |
* parsing a simple command |
||
181 |
*/ |
||
182 |
✗✓✗✗ |
28 |
if (!Flag(FPOSIX) || (cf & CMDWORD)) |
183 |
28 |
cf |= ALIAS; |
|
184 |
} |
||
185 |
|||
186 |
/* Initial state: one of SBASE SHEREDELIM SWORD SASPAREN */ |
||
187 |
289849 |
statep->ls_state = state; |
|
188 |
|||
189 |
/* collect non-special or quoted characters to form word */ |
||
190 |
✓✓✓✓ ✓✓✓✓ ✓✓ |
2305683 |
while (!((c = getsc()) == 0 || |
191 |
((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) { |
||
192 |
✓✓ | 1725989 |
Xcheck(ws, wp); |
193 |
✓✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✓✗ |
1725989 |
switch (state) { |
194 |
case SBASE: |
||
195 |
✗✓✗✗ |
909321 |
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 |
✓✓✓✓ |
909321 |
if (c == '[' && (cf & (VARASN|ARRAYVAR))) { |
262 |
1204 |
*wp = EOS; /* temporary */ |
|
263 |
✓✓ | 1204 |
if (is_wdvarname(Xstring(ws, wp), false)) { |
264 |
char *p, *tmp; |
||
265 |
|||
266 |
✓✗ | 31 |
if (arraysub(&tmp)) { |
267 |
31 |
*wp++ = CHAR; |
|
268 |
31 |
*wp++ = c; |
|
269 |
✓✓ | 344 |
for (p = tmp; *p; ) { |
270 |
✗✓ | 282 |
Xcheck(ws, wp); |
271 |
282 |
*wp++ = CHAR; |
|
272 |
282 |
*wp++ = *p++; |
|
273 |
} |
||
274 |
31 |
afree(tmp, ATEMP); |
|
275 |
31 |
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 |
} |
||
287 |
1173 |
*wp++ = CHAR; |
|
288 |
1173 |
*wp++ = c; |
|
289 |
1173 |
break; |
|
290 |
} |
||
291 |
/* FALLTHROUGH */ |
||
292 |
945292 |
Sbase1: /* includes *(...|...) pattern (*+?@!) */ |
|
293 |
✓✓✓✓ ✓✓ |
945292 |
if (c == '*' || c == '@' || c == '+' || c == '?' || |
294 |
c == '!') { |
||
295 |
✓✓✓✗ |
15077 |
c2 = getsc(); |
296 |
✓✓ | 15077 |
if (c2 == '(' /*)*/ ) { |
297 |
48 |
*wp++ = OPAT; |
|
298 |
48 |
*wp++ = c; |
|
299 |
✗✓ | 48 |
PUSH_STATE(SPATTERN); |
300 |
48 |
break; |
|
301 |
} |
||
302 |
15029 |
ungetsc(c2); |
|
303 |
} |
||
304 |
/* FALLTHROUGH */ |
||
305 |
975451 |
Sbase2: /* doesn't include *(...|...) pattern (*+?@!) */ |
|
306 |
✓✓✓✓ |
975451 |
switch (c) { |
307 |
case '\\': |
||
308 |
✓✓✗✓ |
2056 |
c = getsc(); |
309 |
✓✓ | 2056 |
if (c) /* trailing \ is lost */ |
310 |
2052 |
*wp++ = QCHAR, *wp++ = c; |
|
311 |
break; |
||
312 |
case '\'': |
||
313 |
✓✓ | 5535 |
if ((cf & HEREDOC) || state == SBRACEQ) { |
314 |
22 |
*wp++ = CHAR, *wp++ = c; |
|
315 |
22 |
break; |
|
316 |
} |
||
317 |
5513 |
*wp++ = OQUOTE; |
|
318 |
5513 |
ignore_backslash_newline++; |
|
319 |
✗✓ | 5513 |
PUSH_STATE(SSQUOTE); |
320 |
5513 |
break; |
|
321 |
case '"': |
||
322 |
24753 |
*wp++ = OQUOTE; |
|
323 |
✗✓ | 24753 |
PUSH_STATE(SDQUOTE); |
324 |
24753 |
break; |
|
325 |
default: |
||
326 |
goto Subst; |
||
327 |
} |
||
328 |
break; |
||
329 |
|||
330 |
1334735 |
Subst: |
|
331 |
✓✓✓✓ |
1334735 |
switch (c) { |
332 |
case '\\': |
||
333 |
✓✓✗✓ |
3735 |
c = getsc(); |
334 |
✓✓✓ | 3735 |
switch (c) { |
335 |
case '\\': |
||
336 |
case '$': case '`': |
||
337 |
947 |
*wp++ = QCHAR, *wp++ = c; |
|
338 |
947 |
break; |
|
339 |
case '"': |
||
340 |
✓✗ | 900 |
if ((cf & HEREDOC) == 0) { |
341 |
900 |
*wp++ = QCHAR, *wp++ = c; |
|
342 |
900 |
break; |
|
343 |
} |
||
344 |
/* FALLTHROUGH */ |
||
345 |
default: |
||
346 |
✗✓ | 1888 |
if (cf & UNESCAPE) { |
347 |
*wp++ = QCHAR, *wp++ = c; |
||
348 |
break; |
||
349 |
} |
||
350 |
✗✓ | 1888 |
Xcheck(ws, wp); |
351 |
✓✗ | 1888 |
if (c) { /* trailing \ is lost */ |
352 |
1888 |
*wp++ = CHAR, *wp++ = '\\'; |
|
353 |
1888 |
*wp++ = CHAR, *wp++ = c; |
|
354 |
} |
||
355 |
break; |
||
356 |
} |
||
357 |
break; |
||
358 |
case '$': |
||
359 |
✓✓✓✗ |
58623 |
c = getsc(); |
360 |
✓✓ | 58623 |
if (c == '(') /*)*/ { |
361 |
✓✓✓✗ |
89 |
c = getsc(); |
362 |
✓✓ | 89 |
if (c == '(') /*)*/ { |
363 |
✗✓ | 33 |
PUSH_STATE(SASPAREN); |
364 |
33 |
statep->ls_sasparen.nparen = 2; |
|
365 |
33 |
statep->ls_sasparen.start = |
|
366 |
Xsavepos(ws, wp); |
||
367 |
33 |
*wp++ = EXPRSUB; |
|
368 |
} else { |
||
369 |
56 |
ungetsc(c); |
|
370 |
✗✓ | 56 |
PUSH_STATE(SCSPAREN); |
371 |
56 |
statep->ls_scsparen.nparen = 1; |
|
372 |
56 |
statep->ls_scsparen.csstate = 0; |
|
373 |
56 |
*wp++ = COMSUB; |
|
374 |
} |
||
375 |
✓✓ | 58534 |
} else if (c == '{') /*}*/ { |
376 |
24557 |
*wp++ = OSUBST; |
|
377 |
24557 |
*wp++ = '{'; /*}*/ |
|
378 |
24557 |
wp = get_brace_var(&ws, wp); |
|
379 |
✓✗✓✗ |
24557 |
c = getsc(); |
380 |
/* allow :# and :% (ksh88 compat) */ |
||
381 |
✓✓ | 24557 |
if (c == ':') { |
382 |
76 |
*wp++ = CHAR, *wp++ = c; |
|
383 |
✓✗✓✗ |
76 |
c = getsc(); |
384 |
} |
||
385 |
/* If this is a trim operation, |
||
386 |
* treat (,|,) specially in STBRACE. |
||
387 |
*/ |
||
388 |
✓✓ | 24557 |
if (c == '#' || c == '%') { |
389 |
202 |
ungetsc(c); |
|
390 |
✗✓ | 202 |
PUSH_STATE(STBRACE); |
391 |
} else { |
||
392 |
24355 |
ungetsc(c); |
|
393 |
✓✓ | 24355 |
if (state == SDQUOTE || |
394 |
state == SBRACEQ) |
||
395 |
✗✓ | 12412 |
PUSH_STATE(SBRACEQ); |
396 |
else |
||
397 |
✗✓ | 11943 |
PUSH_STATE(SBRACE); |
398 |
} |
||
399 |
✓✓ | 33977 |
} else if (ctype(c, C_ALPHA)) { |
400 |
31266 |
*wp++ = OSUBST; |
|
401 |
31266 |
*wp++ = 'X'; |
|
402 |
do { |
||
403 |
✓✓ | 242655 |
Xcheck(ws, wp); |
404 |
242655 |
*wp++ = c; |
|
405 |
✓✓✓✗ |
242655 |
c = getsc(); |
406 |
✓✓✓✓ |
242655 |
} while (ctype(c, C_ALPHA) || digit(c)); |
407 |
31266 |
*wp++ = '\0'; |
|
408 |
31266 |
*wp++ = CSUBST; |
|
409 |
31266 |
*wp++ = 'X'; |
|
410 |
31266 |
ungetsc(c); |
|
411 |
✓✓✓✓ |
5162 |
} else if (ctype(c, C_VAR1) || digit(c)) { |
412 |
✗✓ | 2451 |
Xcheck(ws, wp); |
413 |
2451 |
*wp++ = OSUBST; |
|
414 |
2451 |
*wp++ = 'X'; |
|
415 |
2451 |
*wp++ = c; |
|
416 |
2451 |
*wp++ = '\0'; |
|
417 |
2451 |
*wp++ = CSUBST; |
|
418 |
2451 |
*wp++ = 'X'; |
|
419 |
} else { |
||
420 |
260 |
*wp++ = CHAR, *wp++ = '$'; |
|
421 |
260 |
ungetsc(c); |
|
422 |
} |
||
423 |
break; |
||
424 |
case '`': |
||
425 |
✗✓ | 1635 |
PUSH_STATE(SBQUOTE); |
426 |
1635 |
*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 |
1635 |
statep->ls_sbquote.indquotes = 0; |
|
432 |
1635 |
Lex_state *s = statep; |
|
433 |
1635 |
Lex_state *base = state_info.base; |
|
434 |
while (1) { |
||
435 |
✓✓ | 3146 |
for (; s != base; s--) { |
436 |
✓✓ | 3278 |
if (s->ls_state == SDQUOTE) { |
437 |
132 |
statep->ls_sbquote.indquotes = 1; |
|
438 |
132 |
break; |
|
439 |
} |
||
440 |
} |
||
441 |
✓✓ | 1635 |
if (s != base) |
442 |
132 |
break; |
|
443 |
✓✗ | 1503 |
if (!(s = s->ls_info.base)) |
444 |
1503 |
break; |
|
445 |
base = s-- - STATE_BSIZE; |
||
446 |
} |
||
447 |
break; |
||
448 |
default: |
||
449 |
1270742 |
*wp++ = CHAR, *wp++ = c; |
|
450 |
} |
||
451 |
break; |
||
452 |
|||
453 |
case SSQUOTE: |
||
454 |
✓✓ | 244239 |
if (c == '\'') { |
455 |
✗✓ | 5527 |
POP_STATE(); |
456 |
✗✓ | 5527 |
if (state == SBRACEQ) { |
457 |
*wp++ = CHAR, *wp++ = c; |
||
458 |
break; |
||
459 |
} |
||
460 |
5527 |
*wp++ = CQUOTE; |
|
461 |
5527 |
ignore_backslash_newline--; |
|
462 |
} else |
||
463 |
238712 |
*wp++ = QCHAR, *wp++ = c; |
|
464 |
break; |
||
465 |
|||
466 |
case SDQUOTE: |
||
467 |
✓✓ | 251527 |
if (c == '"') { |
468 |
✗✓ | 24753 |
POP_STATE(); |
469 |
24753 |
*wp++ = CQUOTE; |
|
470 |
} else |
||
471 |
226774 |
goto Subst; |
|
472 |
24753 |
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 |
✓✓✓✓ ✗ |
1216 |
switch (statep->ls_scsparen.csstate) { |
481 |
case 0: /* normal */ |
||
482 |
✗✓✓✓ ✓✓ |
908 |
switch (c) { |
483 |
case '(': |
||
484 |
statep->ls_scsparen.nparen++; |
||
485 |
break; |
||
486 |
case ')': |
||
487 |
56 |
statep->ls_scsparen.nparen--; |
|
488 |
56 |
break; |
|
489 |
case '\\': |
||
490 |
2 |
statep->ls_scsparen.csstate = 1; |
|
491 |
2 |
break; |
|
492 |
case '"': |
||
493 |
20 |
statep->ls_scsparen.csstate = 2; |
|
494 |
20 |
break; |
|
495 |
case '\'': |
||
496 |
4 |
statep->ls_scsparen.csstate = 4; |
|
497 |
4 |
ignore_backslash_newline++; |
|
498 |
break; |
||
499 |
} |
||
500 |
break; |
||
501 |
|||
502 |
case 1: /* backslash in normal mode */ |
||
503 |
case 3: /* backslash in double quotes */ |
||
504 |
4 |
--statep->ls_scsparen.csstate; |
|
505 |
4 |
break; |
|
506 |
|||
507 |
case 2: /* double quotes */ |
||
508 |
✓✓ | 260 |
if (c == '"') |
509 |
20 |
statep->ls_scsparen.csstate = 0; |
|
510 |
✓✓ | 240 |
else if (c == '\\') |
511 |
2 |
statep->ls_scsparen.csstate = 3; |
|
512 |
break; |
||
513 |
|||
514 |
case 4: /* single quotes */ |
||
515 |
✓✓ | 44 |
if (c == '\'') { |
516 |
4 |
statep->ls_scsparen.csstate = 0; |
|
517 |
4 |
ignore_backslash_newline--; |
|
518 |
} |
||
519 |
break; |
||
520 |
} |
||
521 |
✓✓ | 1216 |
if (statep->ls_scsparen.nparen == 0) { |
522 |
✗✓ | 56 |
POP_STATE(); |
523 |
56 |
*wp++ = 0; /* end of COMSUB */ |
|
524 |
} else |
||
525 |
1160 |
*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 |
✓✓ | 370 |
if (c == '(') |
533 |
8 |
statep->ls_sasparen.nparen++; |
|
534 |
✓✓ | 362 |
else if (c == ')') { |
535 |
41 |
statep->ls_sasparen.nparen--; |
|
536 |
✓✓ | 41 |
if (statep->ls_sasparen.nparen == 1) { |
537 |
/*(*/ |
||
538 |
✓✓✓✗ ✓✗ |
33 |
if ((c2 = getsc()) == ')') { |
539 |
✗✓ | 33 |
POP_STATE(); |
540 |
33 |
*wp++ = 0; /* end of EXPRSUB */ |
|
541 |
33 |
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 |
337 |
*wp++ = c; |
|
564 |
337 |
break; |
|
565 |
|||
566 |
case SBRACEQ: |
||
567 |
/*{*/ |
||
568 |
✓✓ | 42603 |
if (c == '}') { |
569 |
✗✓ | 12412 |
POP_STATE(); |
570 |
12412 |
*wp++ = CSUBST; |
|
571 |
12412 |
*wp++ = /*{*/ '}'; |
|
572 |
} else |
||
573 |
30191 |
goto Sbase2; |
|
574 |
12412 |
break; |
|
575 |
|||
576 |
case SBRACE: |
||
577 |
/*{*/ |
||
578 |
✓✓ | 48480 |
if (c == '}') { |
579 |
✗✓ | 11943 |
POP_STATE(); |
580 |
11943 |
*wp++ = CSUBST; |
|
581 |
11943 |
*wp++ = /*{*/ '}'; |
|
582 |
} else |
||
583 |
36537 |
goto Sbase1; |
|
584 |
11943 |
break; |
|
585 |
|||
586 |
case STBRACE: |
||
587 |
/* Same as SBRACE, except (,|,) treated specially */ |
||
588 |
/*{*/ |
||
589 |
✓✓ | 732 |
if (c == '}') { |
590 |
✗✓ | 202 |
POP_STATE(); |
591 |
202 |
*wp++ = CSUBST; |
|
592 |
202 |
*wp++ = /*{*/ '}'; |
|
593 |
✓✓ | 530 |
} else if (c == '|') { |
594 |
8 |
*wp++ = SPAT; |
|
595 |
✓✓ | 522 |
} else if (c == '(') { |
596 |
2 |
*wp++ = OPAT; |
|
597 |
2 |
*wp++ = ' '; /* simile for @ */ |
|
598 |
✗✓ | 2 |
PUSH_STATE(SPATTERN); |
599 |
} else |
||
600 |
520 |
goto Sbase1; |
|
601 |
break; |
||
602 |
|||
603 |
case SBQUOTE: |
||
604 |
✓✓ | 57758 |
if (c == '`') { |
605 |
1635 |
*wp++ = 0; |
|
606 |
✗✓ | 1635 |
POP_STATE(); |
607 |
✓✓ | 56123 |
} else if (c == '\\') { |
608 |
✓✓✗✓ ✓✓✓ |
858 |
switch (c = getsc()) { |
609 |
case '\\': |
||
610 |
case '$': case '`': |
||
611 |
137 |
*wp++ = c; |
|
612 |
137 |
break; |
|
613 |
case '"': |
||
614 |
✓✓ | 21 |
if (statep->ls_sbquote.indquotes) { |
615 |
18 |
*wp++ = c; |
|
616 |
18 |
break; |
|
617 |
} |
||
618 |
/* FALLTHROUGH */ |
||
619 |
default: |
||
620 |
✓✗ | 703 |
if (c) { /* trailing \ is lost */ |
621 |
703 |
*wp++ = '\\'; |
|
622 |
703 |
*wp++ = c; |
|
623 |
} |
||
624 |
break; |
||
625 |
} |
||
626 |
} else |
||
627 |
55265 |
*wp++ = c; |
|
628 |
break; |
||
629 |
|||
630 |
case SWORD: /* ONEWORD */ |
||
631 |
goto Subst; |
||
632 |
|||
633 |
case SLETPAREN: /* LETEXPR: (( ... )) */ |
||
634 |
/*(*/ |
||
635 |
✓✓ | 20 |
if (c == ')') { |
636 |
✗✓ | 4 |
if (statep->ls_sletparen.nparen > 0) |
637 |
--statep->ls_sletparen.nparen; |
||
638 |
/*(*/ |
||
639 |
✓✓✓✗ ✓✗ |
4 |
else if ((c2 = getsc()) == ')') { |
640 |
4 |
c = 0; |
|
641 |
4 |
*wp++ = CQUOTE; |
|
642 |
4 |
goto Done; |
|
643 |
} else |
||
644 |
ungetsc(c2); |
||
645 |
✓✗ | 16 |
} 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 |
✓✓ | 4621 |
if (c == '\\') { |
663 |
✓✗✗✓ |
106 |
c = getsc(); |
664 |
✓✗ | 106 |
if (c) { /* trailing \ is lost */ |
665 |
106 |
*wp++ = QCHAR; |
|
666 |
106 |
*wp++ = c; |
|
667 |
} |
||
668 |
✓✓ | 4515 |
} else if (c == '\'') { |
669 |
✗✓ | 14 |
PUSH_STATE(SSQUOTE); |
670 |
14 |
*wp++ = OQUOTE; |
|
671 |
14 |
ignore_backslash_newline++; |
|
672 |
✓✓ | 4501 |
} else if (c == '"') { |
673 |
8 |
state = statep->ls_state = SHEREDQUOTE; |
|
674 |
8 |
*wp++ = OQUOTE; |
|
675 |
} else { |
||
676 |
4493 |
*wp++ = CHAR; |
|
677 |
4493 |
*wp++ = c; |
|
678 |
} |
||
679 |
break; |
||
680 |
|||
681 |
case SHEREDQUOTE: /* " in <<,<<- delimiter */ |
||
682 |
✓✓ | 32 |
if (c == '"') { |
683 |
8 |
*wp++ = CQUOTE; |
|
684 |
8 |
state = statep->ls_state = SHEREDELIM; |
|
685 |
} else { |
||
686 |
✓✓ | 24 |
if (c == '\\') { |
687 |
✓✗✗✓ ✗✓ |
2 |
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 |
24 |
*wp++ = CHAR; |
|
700 |
24 |
*wp++ = c; |
|
701 |
} |
||
702 |
break; |
||
703 |
|||
704 |
case SPATTERN: /* in *(...|...) pattern (*+?@!) */ |
||
705 |
✓✓ | 216 |
if ( /*(*/ c == ')') { |
706 |
58 |
*wp++ = CPAT; |
|
707 |
✗✓ | 58 |
POP_STATE(); |
708 |
✓✓ | 158 |
} else if (c == '|') { |
709 |
32 |
*wp++ = SPAT; |
|
710 |
✓✓ | 126 |
} else if (c == '(') { |
711 |
8 |
*wp++ = OPAT; |
|
712 |
8 |
*wp++ = ' '; /* simile for @ */ |
|
713 |
✗✓ | 8 |
PUSH_STATE(SPATTERN); |
714 |
} else |
||
715 |
118 |
goto Sbase1; |
|
716 |
break; |
||
717 |
} |
||
718 |
} |
||
719 |
289849 |
Done: |
|
720 |
✓✓ | 289849 |
Xcheck(ws, wp); |
721 |
✗✓ | 289849 |
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 |
✓✓ | 289849 |
if (state == SHEREDELIM) |
727 |
840 |
state = SBASE; |
|
728 |
|||
729 |
289849 |
dp = Xstring(ws, wp); |
|
730 |
✓✓✓✗ ✓✓✓✓ ✓✗✓✗ |
289849 |
if ((c == '<' || c == '>') && state == SBASE && |
731 |
((c2 = Xlength(ws, wp)) == 0 || |
||
732 |
(c2 == 2 && dp[0] == CHAR && digit(dp[1])))) { |
||
733 |
10171 |
struct ioword *iop = alloc(sizeof(*iop), ATEMP); |
|
734 |
|||
735 |
✓✓ | 10171 |
if (c2 == 2) |
736 |
2106 |
iop->unit = dp[1] - '0'; |
|
737 |
else |
||
738 |
8065 |
iop->unit = c == '>'; /* 0 for <, 1 for > */ |
|
739 |
|||
740 |
✓✓✓✗ |
10171 |
c2 = getsc(); |
741 |
/* <<, >>, <> are ok, >< is not */ |
||
742 |
✓✓✗✓ |
10171 |
if (c == c2 || (c == '<' && c2 == '>')) { |
743 |
✓✗✓✓ |
1517 |
iop->flag = c == c2 ? |
744 |
(c == '>' ? IOCAT : IOHERE) : IORDWR; |
||
745 |
✓✓ | 1517 |
if (iop->flag == IOHERE) { |
746 |
✓✓✓✗ ✓✓ |
840 |
if ((c2 = getsc()) == '-') |
747 |
30 |
iop->flag |= IOSKIP; |
|
748 |
else |
||
749 |
810 |
ungetsc(c2); |
|
750 |
} |
||
751 |
✓✓ | 8654 |
} else if (c2 == '&') |
752 |
✓✓ | 5530 |
iop->flag = IODUP | (c == '<' ? IORDUP : 0); |
753 |
else { |
||
754 |
✓✓ | 3124 |
iop->flag = c == '>' ? IOWRITE : IOREAD; |
755 |
✗✓ | 3124 |
if (c == '>' && c2 == '|') |
756 |
iop->flag |= IOCLOB; |
||
757 |
else |
||
758 |
3124 |
ungetsc(c2); |
|
759 |
} |
||
760 |
|||
761 |
10171 |
iop->name = NULL; |
|
762 |
10171 |
iop->delim = NULL; |
|
763 |
10171 |
iop->heredoc = NULL; |
|
764 |
10171 |
Xfree(ws, wp); /* free word */ |
|
765 |
10171 |
yylval.iop = iop; |
|
766 |
10171 |
return REDIR; |
|
767 |
} |
||
768 |
|||
769 |
✓✓ | 279678 |
if (wp == dp && state == SBASE) { |
770 |
103565 |
Xfree(ws, wp); /* free word */ |
|
771 |
/* no word, process LEX1 character */ |
||
772 |
✓✓✓✓ ✓ |
103565 |
switch (c) { |
773 |
default: |
||
774 |
17189 |
return c; |
|
775 |
|||
776 |
case '|': |
||
777 |
case '&': |
||
778 |
case ';': |
||
779 |
✓✓✓✗ ✓✓ |
22644 |
if ((c2 = getsc()) == c) |
780 |
✓✓✓✓ ✓✗ |
6029 |
c = (c == ';') ? BREAK : |
781 |
(c == '|') ? LOGOR : |
||
782 |
(c == '&') ? LOGAND : |
||
783 |
YYERRCODE; |
||
784 |
✗✓ | 16615 |
else if (c == '|' && c2 == '&') |
785 |
c = COPROC; |
||
786 |
else |
||
787 |
16615 |
ungetsc(c2); |
|
788 |
22644 |
return c; |
|
789 |
|||
790 |
case '\n': |
||
791 |
56447 |
gethere(); |
|
792 |
✓✓ | 56445 |
if (cf & CONTIN) |
793 |
5985 |
goto Again; |
|
794 |
50460 |
return c; |
|
795 |
|||
796 |
case '(': /*)*/ |
||
797 |
✓✓ | 1964 |
if (!Flag(FSH)) { |
798 |
✓✓✓✗ ✓✓ |
112 |
if ((c2 = getsc()) == '(') /*)*/ |
799 |
/* XXX need to handle ((...); (...)) */ |
||
800 |
4 |
c = MDPAREN; |
|
801 |
else |
||
802 |
108 |
ungetsc(c2); |
|
803 |
} |
||
804 |
1964 |
return c; |
|
805 |
/*(*/ |
||
806 |
case ')': |
||
807 |
5321 |
return c; |
|
808 |
} |
||
809 |
} |
||
810 |
|||
811 |
176113 |
*wp++ = EOS; /* terminate word */ |
|
812 |
176113 |
yylval.cp = Xclose(ws, wp); |
|
813 |
✓✓ | 176113 |
if (state == SWORD || state == SLETPAREN) /* ONEWORD? */ |
814 |
4723 |
return LWORD; |
|
815 |
171390 |
ungetsc(c); /* unget terminator */ |
|
816 |
|||
817 |
/* copy word to unprefixed string ident */ |
||
818 |
✓✓✓✓ |
1188129 |
for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; ) |
819 |
845349 |
*dp++ = *sp++; |
|
820 |
/* Make sure the ident array stays '\0' padded */ |
||
821 |
171390 |
memset(dp, 0, (ident+IDENT) - dp + 1); |
|
822 |
✓✓ | 171390 |
if (c != EOS) |
823 |
51816 |
*ident = '\0'; /* word is not unquoted */ |
|
824 |
|||
825 |
✓✓✓✓ |
171390 |
if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) { |
826 |
struct tbl *p; |
||
827 |
62821 |
int h = hash(ident); |
|
828 |
|||
829 |
/* { */ |
||
830 |
✓✓✓✓ ✓✓✗✓ ✗✗ |
62821 |
if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) && |
831 |
(!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}')) { |
||
832 |
22986 |
afree(yylval.cp, ATEMP); |
|
833 |
22986 |
return p->val.i; |
|
834 |
} |
||
835 |
✓✓✓✓ ✓✗ |
39835 |
if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) && |
836 |
(p->flag & ISSET)) { |
||
837 |
Source *s; |
||
838 |
|||
839 |
✓✓ | 103 |
for (s = source; s->type == SALIAS; s = s->next) |
840 |
✓✓ | 28 |
if (s->u.tblp == p) |
841 |
8 |
return LWORD; |
|
842 |
/* push alias expansion */ |
||
843 |
75 |
s = pushs(SALIAS, source->areap); |
|
844 |
75 |
s->start = s->str = p->val.s; |
|
845 |
75 |
s->u.tblp = p; |
|
846 |
75 |
s->next = source; |
|
847 |
75 |
source = s; |
|
848 |
75 |
afree(yylval.cp, ATEMP); |
|
849 |
75 |
goto Again; |
|
850 |
} |
||
851 |
} |
||
852 |
|||
853 |
148321 |
return LWORD; |
|
854 |
} |
||
855 |
|||
856 |
static void |
||
857 |
gethere(void) |
||
858 |
56447 |
{ |
|
859 |
struct ioword **p; |
||
860 |
|||
861 |
✓✓ | 57285 |
for (p = heres; p < herep; p++) |
862 |
840 |
readhere(*p); |
|
863 |
56445 |
herep = heres; |
|
864 |
56445 |
} |
|
865 |
|||
866 |
/* |
||
867 |
* read "<<word" text into temp file |
||
868 |
*/ |
||
869 |
|||
870 |
static void |
||
871 |
readhere(struct ioword *iop) |
||
872 |
840 |
{ |
|
873 |
int c; |
||
874 |
char *volatile eof; |
||
875 |
char *eofp; |
||
876 |
int skiptabs; |
||
877 |
XString xs; |
||
878 |
char *xp; |
||
879 |
int xpos; |
||
880 |
|||
881 |
840 |
eof = evalstr(iop->delim, 0); |
|
882 |
|||
883 |
✓✓ | 840 |
if (!(iop->flag & IOEVAL)) |
884 |
128 |
ignore_backslash_newline++; |
|
885 |
|||
886 |
840 |
Xinit(xs, xp, 256, ATEMP); |
|
887 |
|||
888 |
for (;;) { |
||
889 |
11796 |
eofp = eof; |
|
890 |
11796 |
skiptabs = iop->flag & IOSKIP; |
|
891 |
11796 |
xpos = Xsavepos(xs, xp); |
|
892 |
✓✓✓✗ ✓✓ |
28406 |
while ((c = getsc()) != 0) { |
893 |
✓✓ | 16608 |
if (skiptabs) { |
894 |
✓✓ | 214 |
if (c == '\t') |
895 |
126 |
continue; |
|
896 |
88 |
skiptabs = 0; |
|
897 |
} |
||
898 |
✓✓ | 16482 |
if (c != *eofp) |
899 |
11794 |
break; |
|
900 |
✓✓ | 4688 |
Xcheck(xs, xp); |
901 |
4688 |
Xput(xs, xp, c); |
|
902 |
4688 |
eofp++; |
|
903 |
} |
||
904 |
/* Allow EOF here so commands with out trailing newlines |
||
905 |
* will work (eg, ksh -c '...', $(...), etc). |
||
906 |
*/ |
||
907 |
✓✓✓✗ |
11796 |
if (*eofp == '\0' && (c == 0 || c == '\n')) { |
908 |
838 |
xp = Xrestpos(xs, xp, xpos); |
|
909 |
break; |
||
910 |
} |
||
911 |
10958 |
ungetsc(c); |
|
912 |
✓✓✓✓ ✓✓ |
294164 |
while ((c = getsc()) != '\n') { |
913 |
✓✓ | 272250 |
if (c == 0) |
914 |
2 |
yyerror("here document `%s' unclosed\n", eof); |
|
915 |
✓✓ | 272248 |
Xcheck(xs, xp); |
916 |
272248 |
Xput(xs, xp, c); |
|
917 |
} |
||
918 |
✓✓ | 10956 |
Xcheck(xs, xp); |
919 |
10956 |
Xput(xs, xp, c); |
|
920 |
10956 |
} |
|
921 |
838 |
Xput(xs, xp, '\0'); |
|
922 |
838 |
iop->heredoc = Xclose(xs, xp); |
|
923 |
|||
924 |
✓✓ | 838 |
if (!(iop->flag & IOEVAL)) |
925 |
128 |
ignore_backslash_newline--; |
|
926 |
838 |
} |
|
927 |
|||
928 |
void |
||
929 |
yyerror(const char *fmt, ...) |
||
930 |
4 |
{ |
|
931 |
va_list va; |
||
932 |
|||
933 |
/* pop aliases and re-reads */ |
||
934 |
✗✓ | 8 |
while (source->type == SALIAS || source->type == SREREAD) |
935 |
source = source->next; |
||
936 |
4 |
source->str = null; /* zap pending input */ |
|
937 |
|||
938 |
4 |
error_prefix(true); |
|
939 |
4 |
va_start(va, fmt); |
|
940 |
4 |
shf_vfprintf(shl_out, fmt, va); |
|
941 |
4 |
va_end(va); |
|
942 |
4 |
errorf(NULL); |
|
943 |
} |
||
944 |
|||
945 |
/* |
||
946 |
* input for yylex with alias expansion |
||
947 |
*/ |
||
948 |
|||
949 |
Source * |
||
950 |
pushs(int type, Area *areap) |
||
951 |
19113 |
{ |
|
952 |
Source *s; |
||
953 |
|||
954 |
19113 |
s = alloc(sizeof(Source), areap); |
|
955 |
19113 |
s->type = type; |
|
956 |
19113 |
s->str = null; |
|
957 |
19113 |
s->start = NULL; |
|
958 |
19113 |
s->line = 0; |
|
959 |
19113 |
s->cmd_offset = 0; |
|
960 |
19113 |
s->errline = 0; |
|
961 |
19113 |
s->file = NULL; |
|
962 |
19113 |
s->flags = 0; |
|
963 |
19113 |
s->next = NULL; |
|
964 |
19113 |
s->areap = areap; |
|
965 |
✓✓ | 19113 |
if (type == SFILE || type == SSTDIN) { |
966 |
char *dummy; |
||
967 |
607 |
Xinit(s->xs, dummy, 256, s->areap); |
|
968 |
} else |
||
969 |
18506 |
memset(&s->xs, 0, sizeof(s->xs)); |
|
970 |
19113 |
return s; |
|
971 |
} |
||
972 |
|||
973 |
static int |
||
974 |
getsc__(void) |
||
975 |
129955 |
{ |
|
976 |
129955 |
Source *s = source; |
|
977 |
int c; |
||
978 |
|||
979 |
✓✓ | 345009 |
while ((c = *s->str++) == 0) { |
980 |
129916 |
s->str = NULL; /* return 0 for EOF by default */ |
|
981 |
✓✓✓✓ ✓✗✓ |
129916 |
switch (s->type) { |
982 |
case SEOF: |
||
983 |
34108 |
s->str = null; |
|
984 |
34108 |
return 0; |
|
985 |
|||
986 |
case SSTDIN: |
||
987 |
case SFILE: |
||
988 |
66834 |
getsc_line(s); |
|
989 |
66834 |
break; |
|
990 |
|||
991 |
case SWSTR: |
||
992 |
break; |
||
993 |
|||
994 |
case SSTRING: |
||
995 |
break; |
||
996 |
|||
997 |
case SWORDS: |
||
998 |
9352 |
s->start = s->str = *s->u.strv++; |
|
999 |
9352 |
s->type = SWORDSEP; |
|
1000 |
9352 |
break; |
|
1001 |
|||
1002 |
case SWORDSEP: |
||
1003 |
✓✓ | 9343 |
if (*s->u.strv == NULL) { |
1004 |
8237 |
s->start = s->str = "\n"; |
|
1005 |
8237 |
s->type = SEOF; |
|
1006 |
} else { |
||
1007 |
1106 |
s->start = s->str = " "; |
|
1008 |
1106 |
s->type = SWORDS; |
|
1009 |
} |
||
1010 |
break; |
||
1011 |
|||
1012 |
case SALIAS: |
||
1013 |
✓✓ | 94 |
if (s->flags & SF_ALIASEND) { |
1014 |
/* pass on an unused SF_ALIAS flag */ |
||
1015 |
19 |
source = s->next; |
|
1016 |
19 |
source->flags |= s->flags & SF_ALIAS; |
|
1017 |
19 |
s = source; |
|
1018 |
✓✗✓✓ |
178 |
} else if (*s->u.tblp->val.s && |
1019 |
isspace((unsigned char)strchr(s->u.tblp->val.s, 0)[-1])) { |
||
1020 |
28 |
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 |
28 |
s->flags |= SF_ALIAS; |
|
1026 |
} 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 |
47 |
source = s->next; /* pop source stack */ |
|
1037 |
47 |
source->flags |= s->flags & SF_ALIAS; |
|
1038 |
47 |
c = getsc__(); |
|
1039 |
✓✓ | 47 |
if (c) { |
1040 |
45 |
s->flags |= SF_ALIASEND; |
|
1041 |
45 |
s->ugbuf[0] = c; s->ugbuf[1] = '\0'; |
|
1042 |
45 |
s->start = s->str = s->ugbuf; |
|
1043 |
45 |
s->next = source; |
|
1044 |
45 |
source = s; |
|
1045 |
} else { |
||
1046 |
2 |
s = source; |
|
1047 |
/* avoid reading eof twice */ |
||
1048 |
2 |
s->str = NULL; |
|
1049 |
2 |
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 |
✓✓ | 95716 |
if (s->str == NULL) { |
1061 |
10709 |
s->type = SEOF; |
|
1062 |
10709 |
s->start = s->str = null; |
|
1063 |
10709 |
return '\0'; |
|
1064 |
} |
||
1065 |
✓✓ | 85007 |
if (s->flags & SF_ECHO) { |
1066 |
2 |
shf_puts(s->str, shl_out); |
|
1067 |
2 |
shf_flush(shl_out); |
|
1068 |
} |
||
1069 |
} |
||
1070 |
85138 |
return c; |
|
1071 |
} |
||
1072 |
|||
1073 |
static void |
||
1074 |
getsc_line(Source *s) |
||
1075 |
66834 |
{ |
|
1076 |
66834 |
char *xp = Xstring(s->xs, xp); |
|
1077 |
✓✓✓✓ |
66834 |
int interactive = Flag(FTALKING) && s->type == SSTDIN; |
1078 |
✓✓✓✓ |
66834 |
int have_tty = interactive && (s->flags & SF_TTY); |
1079 |
|||
1080 |
/* Done here to ensure nothing odd happens when a timeout occurs */ |
||
1081 |
✓✓ | 66834 |
XcheckN(s->xs, xp, LINE); |
1082 |
66834 |
*xp = '\0'; |
|
1083 |
66834 |
s->start = s->str = xp; |
|
1084 |
|||
1085 |
✓✓✗✓ |
66834 |
if (have_tty && ksh_tmout) { |
1086 |
ksh_tmout_state = TMOUT_READING; |
||
1087 |
alarm(ksh_tmout); |
||
1088 |
} |
||
1089 |
#ifdef EDIT |
||
1090 |
✓✓✓✗ ✗✓✗✗ |
66855 |
if (have_tty && (0 |
1091 |
# ifdef VI |
||
1092 |
|| Flag(FVI) |
||
1093 |
# endif /* VI */ |
||
1094 |
# ifdef EMACS |
||
1095 |
|| Flag(FEMACS) || Flag(FGMACS) |
||
1096 |
# endif /* EMACS */ |
||
1097 |
)) { |
||
1098 |
int nread; |
||
1099 |
|||
1100 |
21 |
nread = x_read(xp, LINE); |
|
1101 |
✗✓ | 21 |
if (nread < 0) /* read error */ |
1102 |
nread = 0; |
||
1103 |
21 |
xp[nread] = '\0'; |
|
1104 |
21 |
xp += nread; |
|
1105 |
} |
||
1106 |
else |
||
1107 |
#endif /* EDIT */ |
||
1108 |
{ |
||
1109 |
✓✓ | 66813 |
if (interactive) { |
1110 |
278 |
pprompt(prompt, 0); |
|
1111 |
} else |
||
1112 |
66535 |
s->line++; |
|
1113 |
|||
1114 |
while (1) { |
||
1115 |
66823 |
char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf); |
|
1116 |
|||
1117 |
✓✓✗✓ ✗✗ |
66823 |
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 |
✓✓✓✓ |
66823 |
if (!p || (xp = p, xp[-1] == '\n')) |
1125 |
break; |
||
1126 |
/* double buffer size */ |
||
1127 |
10 |
xp++; /* move past null so doubling works... */ |
|
1128 |
✓✓ | 10 |
XcheckN(s->xs, xp, Xlength(s->xs, xp)); |
1129 |
10 |
xp--; /* ...and move back again */ |
|
1130 |
} |
||
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 |
✓✓ | 66813 |
if (s->type == SSTDIN) |
1138 |
3030 |
shf_flush(s->u.shf); |
|
1139 |
} |
||
1140 |
/* XXX: temporary kludge to restore source after a |
||
1141 |
* trap may have been executed. |
||
1142 |
*/ |
||
1143 |
66834 |
source = s; |
|
1144 |
✓✓✗✓ |
66834 |
if (have_tty && ksh_tmout) { |
1145 |
ksh_tmout_state = TMOUT_EXECUTING; |
||
1146 |
alarm(0); |
||
1147 |
} |
||
1148 |
66834 |
s->start = s->str = Xstring(s->xs, xp); |
|
1149 |
66834 |
strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp)); |
|
1150 |
/* Note: if input is all nulls, this is not eof */ |
||
1151 |
✓✓ | 66834 |
if (Xlength(s->xs, xp) == 0) { /* EOF */ |
1152 |
✓✓ | 514 |
if (s->type == SFILE) |
1153 |
104 |
shf_fdclose(s->u.shf); |
|
1154 |
514 |
s->str = NULL; |
|
1155 |
✓✓ | 66320 |
} else if (interactive) { |
1156 |
#ifdef HISTORY |
||
1157 |
239 |
char *p = Xstring(s->xs, xp); |
|
1158 |
✓✓ | 239 |
if (cur_prompt == PS1) |
1159 |
✓✓✓✓ |
244 |
while (*p && ctype(*p, C_IFS) && ctype(*p, C_IFSWS)) |
1160 |
9 |
p++; |
|
1161 |
✓✓ | 239 |
if (*p) { |
1162 |
230 |
s->line++; |
|
1163 |
230 |
histsave(s->line, s->str, 1); |
|
1164 |
} |
||
1165 |
#endif /* HISTORY */ |
||
1166 |
} |
||
1167 |
✓✓ | 66834 |
if (interactive) |
1168 |
299 |
set_prompt(PS2, NULL); |
|
1169 |
66834 |
} |
|
1170 |
|||
1171 |
static char * |
||
1172 |
special_prompt_expand(char *str) |
||
1173 |
295 |
{ |
|
1174 |
295 |
char *p = str; |
|
1175 |
|||
1176 |
✗✓ | 590 |
while ((p = strstr(p, "\\$")) != NULL) { |
1177 |
*(p+1) = 'p'; |
||
1178 |
} |
||
1179 |
295 |
return str; |
|
1180 |
} |
||
1181 |
|||
1182 |
void |
||
1183 |
set_prompt(int to, Source *s) |
||
1184 |
594 |
{ |
|
1185 |
char *ps1; |
||
1186 |
Area *saved_atemp; |
||
1187 |
|||
1188 |
594 |
cur_prompt = to; |
|
1189 |
|||
1190 |
✓✓✗ | 594 |
switch (to) { |
1191 |
case PS1: /* command */ |
||
1192 |
295 |
ps1 = str_save(str_val(global("PS1")), ATEMP); |
|
1193 |
295 |
saved_atemp = ATEMP; /* ps1 is freed by substitute() */ |
|
1194 |
295 |
newenv(E_ERRH); |
|
1195 |
✗✓ | 295 |
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 |
295 |
char *tmp = special_prompt_expand(ps1); |
|
1206 |
295 |
prompt = str_save(substitute(tmp, 0), saved_atemp); |
|
1207 |
} |
||
1208 |
295 |
quitenv(NULL); |
|
1209 |
295 |
break; |
|
1210 |
case PS2: /* command continuation */ |
||
1211 |
299 |
prompt = str_val(global("PS2")); |
|
1212 |
break; |
||
1213 |
} |
||
1214 |
594 |
} |
|
1215 |
|||
1216 |
static int |
||
1217 |
dopprompt(const char *sp, int ntruncate, const char **spp, int doprint) |
||
1218 |
344 |
{ |
|
1219 |
344 |
char strbuf[1024], tmpbuf[1024], *p, *str, nbuf[32], delimiter = '\0'; |
|
1220 |
344 |
int len, c, n, totlen = 0, indelimit = 0, counting = 1, delimitthis; |
|
1221 |
344 |
const char *cp = sp; |
|
1222 |
struct tm *tm; |
||
1223 |
time_t t; |
||
1224 |
|||
1225 |
✓✓✗✓ |
344 |
if (*cp && cp[1] == '\r') { |
1226 |
delimiter = *cp; |
||
1227 |
cp += 2; |
||
1228 |
} |
||
1229 |
|||
1230 |
✓✓ | 758 |
while (*cp != 0) { |
1231 |
414 |
delimitthis = 0; |
|
1232 |
✗✓✗✗ |
414 |
if (indelimit && *cp != delimiter) |
1233 |
; |
||
1234 |
✗✓ | 414 |
else if (*cp == '\n' || *cp == '\r') { |
1235 |
totlen = 0; |
||
1236 |
sp = cp + 1; |
||
1237 |
✗✓ | 414 |
} else if (*cp == '\t') { |
1238 |
if (counting) |
||
1239 |
totlen = (totlen | 7) + 1; |
||
1240 |
✗✓ | 414 |
} else if (*cp == delimiter) { |
1241 |
indelimit = !indelimit; |
||
1242 |
delimitthis = 1; |
||
1243 |
} |
||
1244 |
|||
1245 |
✓✓ | 414 |
if (*cp == '\\') { |
1246 |
10 |
cp++; |
|
1247 |
✗✓ | 10 |
if (!*cp) |
1248 |
break; |
||
1249 |
✗✓ | 10 |
if (Flag(FSH)) |
1250 |
snprintf(strbuf, sizeof strbuf, "\\%c", *cp); |
||
1251 |
✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✓ ✗✗✗✗ |
10 |
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 |
✓✗✗✓ |
10 |
if ((cp[1] > '7' || cp[1] < '0') || |
1398 |
(cp[2] > '7' || cp[2] < '0')) { |
||
1399 |
snprintf(strbuf, sizeof strbuf, |
||
1400 |
"\\%c", *cp); |
||
1401 |
break; |
||
1402 |
} |
||
1403 |
10 |
n = (cp[0] - '0') * 8 * 8 + (cp[1] - '0') * 8 + |
|
1404 |
(cp[2] - '0'); |
||
1405 |
10 |
snprintf(strbuf, sizeof strbuf, "%c", n); |
|
1406 |
10 |
cp += 2; |
|
1407 |
10 |
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 |
10 |
cp++; |
|
1426 |
|||
1427 |
10 |
str = strbuf; |
|
1428 |
10 |
len = strlen(str); |
|
1429 |
✗✓ | 10 |
if (ntruncate) { |
1430 |
if (ntruncate >= len) { |
||
1431 |
ntruncate -= len; |
||
1432 |
continue; |
||
1433 |
} |
||
1434 |
str += ntruncate; |
||
1435 |
len -= ntruncate; |
||
1436 |
ntruncate = 0; |
||
1437 |
} |
||
1438 |
✓✗ | 10 |
if (doprint) |
1439 |
10 |
shf_write(str, len, shl_out); |
|
1440 |
✓✗✓✗ |
10 |
if (counting && !indelimit && !delimitthis) |
1441 |
10 |
totlen += len; |
|
1442 |
continue; |
||
1443 |
✓✗ | 404 |
} else if (*cp != '!') |
1444 |
404 |
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 |
✗✓ | 404 |
if (counting && ntruncate) |
1469 |
--ntruncate; |
||
1470 |
✓✓ | 404 |
else if (doprint) { |
1471 |
✓✓ | 338 |
shf_putc(c, shl_out); |
1472 |
} |
||
1473 |
✓✗✓✗ |
404 |
if (counting && !indelimit && !delimitthis) |
1474 |
404 |
totlen++; |
|
1475 |
} |
||
1476 |
✓✓ | 344 |
if (doprint) |
1477 |
311 |
shf_flush(shl_out); |
|
1478 |
✓✓ | 344 |
if (spp) |
1479 |
21 |
*spp = sp; |
|
1480 |
344 |
return (totlen); |
|
1481 |
} |
||
1482 |
|||
1483 |
void |
||
1484 |
pprompt(const char *cp, int ntruncate) |
||
1485 |
311 |
{ |
|
1486 |
311 |
dopprompt(cp, ntruncate, NULL, 1); |
|
1487 |
311 |
} |
|
1488 |
|||
1489 |
int |
||
1490 |
promptlen(const char *cp, const char **spp) |
||
1491 |
33 |
{ |
|
1492 |
33 |
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 |
24557 |
{ |
|
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 |
24557 |
state = PS_INITIAL; |
|
1509 |
while (1) { |
||
1510 |
✓✓✓✗ |
162801 |
c = getsc(); |
1511 |
/* State machine to figure out where the variable part ends. */ |
||
1512 |
✓✓✓✓ ✓✗ |
162801 |
switch (state) { |
1513 |
case PS_INITIAL: |
||
1514 |
✓✓ | 24557 |
if (c == '#') { |
1515 |
8 |
state = PS_SAW_HASH; |
|
1516 |
8 |
break; |
|
1517 |
} |
||
1518 |
/* FALLTHROUGH */ |
||
1519 |
case PS_SAW_HASH: |
||
1520 |
✓✓ | 24557 |
if (letter(c)) |
1521 |
24534 |
state = PS_IDENT; |
|
1522 |
✓✓ | 23 |
else if (digit(c)) |
1523 |
19 |
state = PS_NUMBER; |
|
1524 |
✓✓ | 4 |
else if (ctype(c, C_VAR1)) |
1525 |
2 |
state = PS_VAR1; |
|
1526 |
else |
||
1527 |
2 |
state = PS_END; |
|
1528 |
break; |
||
1529 |
case PS_IDENT: |
||
1530 |
✓✓✓✓ |
138215 |
if (!letnum(c)) { |
1531 |
24534 |
state = PS_END; |
|
1532 |
✓✓ | 24534 |
if (c == '[') { |
1533 |
char *tmp, *p; |
||
1534 |
|||
1535 |
✗✓ | 26 |
if (!arraysub(&tmp)) |
1536 |
yyerror("missing ]\n"); |
||
1537 |
26 |
*wp++ = c; |
|
1538 |
✓✓ | 132 |
for (p = tmp; *p; ) { |
1539 |
✗✓ | 80 |
Xcheck(*wsp, wp); |
1540 |
80 |
*wp++ = *p++; |
|
1541 |
} |
||
1542 |
26 |
afree(tmp, ATEMP); |
|
1543 |
✓✗✓✗ |
26 |
c = getsc(); /* the ] */ |
1544 |
} |
||
1545 |
} |
||
1546 |
break; |
||
1547 |
case PS_NUMBER: |
||
1548 |
✓✗ | 19 |
if (!digit(c)) |
1549 |
19 |
state = PS_END; |
|
1550 |
break; |
||
1551 |
case PS_VAR1: |
||
1552 |
2 |
state = PS_END; |
|
1553 |
break; |
||
1554 |
case PS_END: /* keep gcc happy */ |
||
1555 |
break; |
||
1556 |
} |
||
1557 |
✓✓ | 162801 |
if (state == PS_END) { |
1558 |
24557 |
*wp++ = '\0'; /* end of variable part */ |
|
1559 |
24557 |
ungetsc(c); |
|
1560 |
break; |
||
1561 |
} |
||
1562 |
✓✓ | 138244 |
Xcheck(*wsp, wp); |
1563 |
138244 |
*wp++ = c; |
|
1564 |
138244 |
} |
|
1565 |
24557 |
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 |
57 |
{ |
|
1576 |
XString ws; |
||
1577 |
char *wp; |
||
1578 |
char c; |
||
1579 |
57 |
int depth = 1; /* we are just past the initial [ */ |
|
1580 |
|||
1581 |
57 |
Xinit(ws, wp, 32, ATEMP); |
|
1582 |
|||
1583 |
do { |
||
1584 |
✓✗✓✗ |
362 |
c = getsc(); |
1585 |
✗✓ | 362 |
Xcheck(ws, wp); |
1586 |
362 |
*wp++ = c; |
|
1587 |
✓✓ | 362 |
if (c == '[') |
1588 |
21 |
depth++; |
|
1589 |
✓✓ | 341 |
else if (c == ']') |
1590 |
78 |
depth--; |
|
1591 |
✓✓✓✗ |
362 |
} while (depth > 0 && c && c != '\n'); |
1592 |
|||
1593 |
57 |
*wp++ = '\0'; |
|
1594 |
57 |
*strp = Xclose(ws, wp); |
|
1595 |
|||
1596 |
57 |
return depth == 0 ? 1 : 0; |
|
1597 |
} |
||
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 |
592262 |
{ |
|
1603 |
✓✓ | 592262 |
if (backslash_skip) |
1604 |
1507 |
backslash_skip--; |
|
1605 |
/* Don't unget eof... */ |
||
1606 |
✓✓ | 592262 |
if (source->str == null && c == '\0') |
1607 |
22899 |
return source->str; |
|
1608 |
✓✗ | 569363 |
if (source->str > source->start) |
1609 |
569363 |
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 |
569363 |
return source->str; |
|
1620 |
} |
||
1621 |
|||
1622 |
|||
1623 |
/* Called to get a char that isn't a \newline sequence. */ |
||
1624 |
static int |
||
1625 |
getsc_bn(void) |
||
1626 |
156284 |
{ |
|
1627 |
int c, c2; |
||
1628 |
|||
1629 |
✓✓ | 156284 |
if (ignore_backslash_newline) |
1630 |
✓✓ | 13581 |
return getsc_(); |
1631 |
|||
1632 |
✓✓ | 142703 |
if (backslash_skip == 1) { |
1633 |
6899 |
backslash_skip = 2; |
|
1634 |
✓✓ | 6899 |
return getsc_(); |
1635 |
} |
||
1636 |
|||
1637 |
135804 |
backslash_skip = 0; |
|
1638 |
|||
1639 |
while (1) { |
||
1640 |
✓✓ | 137185 |
c = getsc_(); |
1641 |
✓✓ | 137185 |
if (c == '\\') { |
1642 |
✓✓✓✓ |
9787 |
if ((c2 = getsc_()) == '\n') |
1643 |
/* ignore the \newline; get the next char... */ |
||
1644 |
1381 |
continue; |
|
1645 |
8406 |
ungetsc(c2); |
|
1646 |
8406 |
backslash_skip = 1; |
|
1647 |
} |
||
1648 |
135804 |
return c; |
|
1649 |
} |
||
1650 |
} |
||
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) |