2 |
/* |
/* |
3 |
* ash shell port for busybox |
* ash shell port for busybox |
4 |
* |
* |
5 |
|
* This code is derived from software contributed to Berkeley by |
6 |
|
* Kenneth Almquist. |
7 |
|
* |
8 |
|
* Original BSD copyright notice is retained at the end of this file. |
9 |
|
* |
10 |
* Copyright (c) 1989, 1991, 1993, 1994 |
* Copyright (c) 1989, 1991, 1993, 1994 |
11 |
* The Regents of the University of California. All rights reserved. |
* The Regents of the University of California. All rights reserved. |
12 |
* |
* |
13 |
* Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au> |
* Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au> |
14 |
* was re-ported from NetBSD and debianized. |
* was re-ported from NetBSD and debianized. |
15 |
* |
* |
|
* This code is derived from software contributed to Berkeley by |
|
|
* Kenneth Almquist. |
|
|
* |
|
16 |
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
|
* |
|
|
* Original BSD copyright notice is retained at the end of this file. |
|
|
*/ |
|
|
|
|
|
/* |
|
|
* rewrite arith.y to micro stack based cryptic algorithm by |
|
|
* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com> |
|
|
* |
|
|
* Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support |
|
|
* dynamic variables. |
|
|
* |
|
|
* Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be |
|
|
* used in busybox and size optimizations, |
|
|
* rewrote arith (see notes to this), added locale support, |
|
|
* rewrote dynamic variables. |
|
17 |
*/ |
*/ |
18 |
|
|
19 |
/* |
/* |
34 |
|
|
35 |
#define PROFILE 0 |
#define PROFILE 0 |
36 |
|
|
|
#define IFS_BROKEN |
|
|
|
|
37 |
#define JOBS ENABLE_ASH_JOB_CONTROL |
#define JOBS ENABLE_ASH_JOB_CONTROL |
38 |
|
|
39 |
#if DEBUG |
#if DEBUG |
46 |
#include <paths.h> |
#include <paths.h> |
47 |
#include <setjmp.h> |
#include <setjmp.h> |
48 |
#include <fnmatch.h> |
#include <fnmatch.h> |
49 |
#if JOBS || ENABLE_ASH_READ_NCHARS |
|
50 |
# include <termios.h> |
#include "shell_common.h" |
51 |
|
#include "builtin_read.h" |
52 |
|
#include "math.h" |
53 |
|
#if ENABLE_ASH_RANDOM_SUPPORT |
54 |
|
# include "random.h" |
55 |
|
#else |
56 |
|
# define CLEAR_RANDOM_T(rnd) ((void)0) |
57 |
|
#endif |
58 |
|
|
59 |
|
#define SKIP_definitions 1 |
60 |
|
#include "applet_tables.h" |
61 |
|
#undef SKIP_definitions |
62 |
|
#if NUM_APPLETS == 1 |
63 |
|
/* STANDALONE does not make sense, and won't compile */ |
64 |
|
# undef CONFIG_FEATURE_SH_STANDALONE |
65 |
|
# undef ENABLE_FEATURE_SH_STANDALONE |
66 |
|
# undef IF_FEATURE_SH_STANDALONE |
67 |
|
# undef IF_NOT_FEATURE_SH_STANDALONE |
68 |
|
# define ENABLE_FEATURE_SH_STANDALONE 0 |
69 |
|
# define IF_FEATURE_SH_STANDALONE(...) |
70 |
|
# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__ |
71 |
#endif |
#endif |
72 |
|
|
73 |
#ifndef PIPE_BUF |
#ifndef PIPE_BUF |
75 |
#endif |
#endif |
76 |
|
|
77 |
#if defined(__uClinux__) |
#if defined(__uClinux__) |
78 |
# error "Do not even bother, ash will not run on uClinux" |
# error "Do not even bother, ash will not run on NOMMU machine" |
79 |
#endif |
#endif |
80 |
|
|
81 |
|
|
103 |
"b" "notify", |
"b" "notify", |
104 |
"u" "nounset", |
"u" "nounset", |
105 |
"\0" "vi" |
"\0" "vi" |
106 |
|
#if ENABLE_ASH_BASH_COMPAT |
107 |
|
,"\0" "pipefail" |
108 |
|
#endif |
109 |
#if DEBUG |
#if DEBUG |
110 |
,"\0" "nolog" |
,"\0" "nolog" |
111 |
,"\0" "debug" |
,"\0" "debug" |
112 |
#endif |
#endif |
113 |
}; |
}; |
114 |
|
|
115 |
#define optletters(n) optletters_optnames[(n)][0] |
#define optletters(n) optletters_optnames[n][0] |
116 |
#define optnames(n) (&optletters_optnames[(n)][1]) |
#define optnames(n) (optletters_optnames[n] + 1) |
117 |
|
|
118 |
enum { NOPTS = ARRAY_SIZE(optletters_optnames) }; |
enum { NOPTS = ARRAY_SIZE(optletters_optnames) }; |
119 |
|
|
122 |
|
|
123 |
static const char homestr[] ALIGN1 = "HOME"; |
static const char homestr[] ALIGN1 = "HOME"; |
124 |
static const char snlfmt[] ALIGN1 = "%s\n"; |
static const char snlfmt[] ALIGN1 = "%s\n"; |
125 |
static const char illnum[] ALIGN1 = "Illegal number: %s"; |
static const char msg_illnum[] ALIGN1 = "Illegal number: %s"; |
126 |
|
|
127 |
/* |
/* |
128 |
* We enclose jmp_buf in a structure so that we can declare pointers to |
* We enclose jmp_buf in a structure so that we can declare pointers to |
129 |
* jump locations. The global variable handler contains the location to |
* jump locations. The global variable handler contains the location to |
130 |
* jump to when an exception occurs, and the global variable exception |
* jump to when an exception occurs, and the global variable exception_type |
131 |
* contains a code identifying the exception. To implement nested |
* contains a code identifying the exception. To implement nested |
132 |
* exception handlers, the user should save the value of handler on entry |
* exception handlers, the user should save the value of handler on entry |
133 |
* to an inner scope, set handler to point to a jmploc structure for the |
* to an inner scope, set handler to point to a jmploc structure for the |
152 |
|
|
153 |
struct jmploc *exception_handler; |
struct jmploc *exception_handler; |
154 |
|
|
155 |
// disabled by vda: cannot understand how it was supposed to work - |
volatile int suppress_int; /* counter */ |
156 |
// cannot fix bugs. That's why you have to explain your non-trivial designs! |
volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ |
|
// /* do we generate EXSIG events */ |
|
|
// int exsig; /* counter */ |
|
|
volatile int suppressint; /* counter */ |
|
|
volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */ |
|
157 |
/* last pending signal */ |
/* last pending signal */ |
158 |
volatile /*sig_atomic_t*/ smallint pendingsig; |
volatile /*sig_atomic_t*/ smallint pending_sig; |
159 |
smallint exception; /* kind of exception (0..5) */ |
smallint exception_type; /* kind of exception (0..5) */ |
160 |
/* exceptions */ |
/* exceptions */ |
161 |
#define EXINT 0 /* SIGINT received */ |
#define EXINT 0 /* SIGINT received */ |
162 |
#define EXERROR 1 /* a generic error */ |
#define EXERROR 1 /* a generic error */ |
183 |
#define bflag optlist[11] |
#define bflag optlist[11] |
184 |
#define uflag optlist[12] |
#define uflag optlist[12] |
185 |
#define viflag optlist[13] |
#define viflag optlist[13] |
186 |
|
#if ENABLE_ASH_BASH_COMPAT |
187 |
|
# define pipefail optlist[14] |
188 |
|
#else |
189 |
|
# define pipefail 0 |
190 |
|
#endif |
191 |
#if DEBUG |
#if DEBUG |
192 |
#define nolog optlist[14] |
# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT] |
193 |
#define debug optlist[15] |
# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT] |
194 |
#endif |
#endif |
195 |
|
|
196 |
/* trap handler commands */ |
/* trap handler commands */ |
197 |
/* |
/* |
198 |
* Sigmode records the current value of the signal handlers for the various |
* Sigmode records the current value of the signal handlers for the various |
199 |
* modes. A value of zero means that the current handler is not known. |
* modes. A value of zero means that the current handler is not known. |
200 |
* S_HARD_IGN indicates that the signal was ignored on entry to the shell, |
* S_HARD_IGN indicates that the signal was ignored on entry to the shell. |
201 |
*/ |
*/ |
202 |
char sigmode[NSIG - 1]; |
char sigmode[NSIG - 1]; |
203 |
#define S_DFL 1 /* default signal handling (SIG_DFL) */ |
#define S_DFL 1 /* default signal handling (SIG_DFL) */ |
204 |
#define S_CATCH 2 /* signal is caught */ |
#define S_CATCH 2 /* signal is caught */ |
205 |
#define S_IGN 3 /* signal is ignored (SIG_IGN) */ |
#define S_IGN 3 /* signal is ignored (SIG_IGN) */ |
206 |
#define S_HARD_IGN 4 /* signal is ignored permenantly */ |
#define S_HARD_IGN 4 /* signal is ignored permenantly */ |
|
#define S_RESET 5 /* temporary - to reset a hard ignored sig */ |
|
207 |
|
|
208 |
/* indicates specified signal received */ |
/* indicates specified signal received */ |
209 |
char gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ |
uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ |
210 |
char *trap[NSIG]; |
char *trap[NSIG]; |
211 |
|
char **trap_ptr; /* used only by "trap hack" */ |
212 |
|
|
213 |
/* Rarely referenced stuff */ |
/* Rarely referenced stuff */ |
214 |
#if ENABLE_ASH_RANDOM_SUPPORT |
#if ENABLE_ASH_RANDOM_SUPPORT |
215 |
/* Random number generators */ |
random_t random_gen; |
|
int32_t random_galois_LFSR; /* Galois LFSR (fast but weak). signed! */ |
|
|
uint32_t random_LCG; /* LCG (fast but weak) */ |
|
216 |
#endif |
#endif |
217 |
pid_t backgndpid; /* pid of last background process */ |
pid_t backgndpid; /* pid of last background process */ |
218 |
smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ |
smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ |
226 |
#define physdir (G_misc.physdir ) |
#define physdir (G_misc.physdir ) |
227 |
#define arg0 (G_misc.arg0 ) |
#define arg0 (G_misc.arg0 ) |
228 |
#define exception_handler (G_misc.exception_handler) |
#define exception_handler (G_misc.exception_handler) |
229 |
#define exception (G_misc.exception ) |
#define exception_type (G_misc.exception_type ) |
230 |
#define suppressint (G_misc.suppressint ) |
#define suppress_int (G_misc.suppress_int ) |
231 |
#define intpending (G_misc.intpending ) |
#define pending_int (G_misc.pending_int ) |
232 |
//#define exsig (G_misc.exsig ) |
#define pending_sig (G_misc.pending_sig ) |
|
#define pendingsig (G_misc.pendingsig ) |
|
233 |
#define isloginsh (G_misc.isloginsh ) |
#define isloginsh (G_misc.isloginsh ) |
234 |
#define nullstr (G_misc.nullstr ) |
#define nullstr (G_misc.nullstr ) |
235 |
#define optlist (G_misc.optlist ) |
#define optlist (G_misc.optlist ) |
236 |
#define sigmode (G_misc.sigmode ) |
#define sigmode (G_misc.sigmode ) |
237 |
#define gotsig (G_misc.gotsig ) |
#define gotsig (G_misc.gotsig ) |
238 |
#define trap (G_misc.trap ) |
#define trap (G_misc.trap ) |
239 |
#define random_galois_LFSR (G_misc.random_galois_LFSR) |
#define trap_ptr (G_misc.trap_ptr ) |
240 |
#define random_LCG (G_misc.random_LCG ) |
#define random_gen (G_misc.random_gen ) |
241 |
#define backgndpid (G_misc.backgndpid ) |
#define backgndpid (G_misc.backgndpid ) |
242 |
#define job_warning (G_misc.job_warning) |
#define job_warning (G_misc.job_warning) |
243 |
#define INIT_G_misc() do { \ |
#define INIT_G_misc() do { \ |
245 |
barrier(); \ |
barrier(); \ |
246 |
curdir = nullstr; \ |
curdir = nullstr; \ |
247 |
physdir = nullstr; \ |
physdir = nullstr; \ |
248 |
|
trap_ptr = trap; \ |
249 |
} while (0) |
} while (0) |
250 |
|
|
251 |
|
|
255 |
static void trace_vprintf(const char *fmt, va_list va); |
static void trace_vprintf(const char *fmt, va_list va); |
256 |
# define TRACE(param) trace_printf param |
# define TRACE(param) trace_printf param |
257 |
# define TRACEV(param) trace_vprintf param |
# define TRACEV(param) trace_vprintf param |
258 |
# define close(f) do { \ |
# define close(fd) do { \ |
259 |
int dfd = (f); \ |
int dfd = (fd); \ |
260 |
if (close(dfd) < 0) \ |
if (close(dfd) < 0) \ |
261 |
bb_error_msg("bug on %d: closing %d(%x)", \ |
bb_error_msg("bug on %d: closing %d(0x%x)", \ |
262 |
__LINE__, dfd, dfd); \ |
__LINE__, dfd, dfd); \ |
263 |
} while (0) |
} while (0) |
264 |
#else |
#else |
270 |
/* ============ Utility functions */ |
/* ============ Utility functions */ |
271 |
#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0) |
#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0) |
272 |
|
|
|
/* C99 say: "char" declaration may be signed or unsigned by default */ |
|
|
#define signed_char2int(sc) ((int)(signed char)(sc)) |
|
|
|
|
273 |
static int isdigit_str9(const char *str) |
static int isdigit_str9(const char *str) |
274 |
{ |
{ |
275 |
int maxlen = 9 + 1; /* max 9 digits: 999999999 */ |
int maxlen = 9 + 1; /* max 9 digits: 999999999 */ |
287 |
* more fun than worrying about efficiency and portability. :-)) |
* more fun than worrying about efficiency and portability. :-)) |
288 |
*/ |
*/ |
289 |
#define INT_OFF do { \ |
#define INT_OFF do { \ |
290 |
suppressint++; \ |
suppress_int++; \ |
291 |
xbarrier(); \ |
xbarrier(); \ |
292 |
} while (0) |
} while (0) |
293 |
|
|
294 |
/* |
/* |
295 |
* Called to raise an exception. Since C doesn't include exceptions, we |
* Called to raise an exception. Since C doesn't include exceptions, we |
296 |
* just do a longjmp to the exception handler. The type of exception is |
* just do a longjmp to the exception handler. The type of exception is |
297 |
* stored in the global variable "exception". |
* stored in the global variable "exception_type". |
298 |
*/ |
*/ |
299 |
static void raise_exception(int) NORETURN; |
static void raise_exception(int) NORETURN; |
300 |
static void |
static void |
305 |
abort(); |
abort(); |
306 |
#endif |
#endif |
307 |
INT_OFF; |
INT_OFF; |
308 |
exception = e; |
exception_type = e; |
309 |
longjmp(exception_handler->loc, 1); |
longjmp(exception_handler->loc, 1); |
310 |
} |
} |
311 |
#if DEBUG |
#if DEBUG |
326 |
static void |
static void |
327 |
raise_interrupt(void) |
raise_interrupt(void) |
328 |
{ |
{ |
329 |
int i; |
int ex_type; |
330 |
|
|
331 |
intpending = 0; |
pending_int = 0; |
332 |
/* Signal is not automatically unmasked after it is raised, |
/* Signal is not automatically unmasked after it is raised, |
333 |
* do it ourself - unmask all signals */ |
* do it ourself - unmask all signals */ |
334 |
sigprocmask_allsigs(SIG_UNBLOCK); |
sigprocmask_allsigs(SIG_UNBLOCK); |
335 |
/* pendingsig = 0; - now done in onsig() */ |
/* pending_sig = 0; - now done in onsig() */ |
336 |
|
|
337 |
i = EXSIG; |
ex_type = EXSIG; |
338 |
if (gotsig[SIGINT - 1] && !trap[SIGINT]) { |
if (gotsig[SIGINT - 1] && !trap[SIGINT]) { |
339 |
if (!(rootshell && iflag)) { |
if (!(rootshell && iflag)) { |
340 |
/* Kill ourself with SIGINT */ |
/* Kill ourself with SIGINT */ |
341 |
signal(SIGINT, SIG_DFL); |
signal(SIGINT, SIG_DFL); |
342 |
raise(SIGINT); |
raise(SIGINT); |
343 |
} |
} |
344 |
i = EXINT; |
ex_type = EXINT; |
345 |
} |
} |
346 |
raise_exception(i); |
raise_exception(ex_type); |
347 |
/* NOTREACHED */ |
/* NOTREACHED */ |
348 |
} |
} |
349 |
#if DEBUG |
#if DEBUG |
353 |
} while (0) |
} while (0) |
354 |
#endif |
#endif |
355 |
|
|
356 |
#if ENABLE_ASH_OPTIMIZE_FOR_SIZE |
static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void |
|
static void |
|
357 |
int_on(void) |
int_on(void) |
358 |
{ |
{ |
359 |
if (--suppressint == 0 && intpending) { |
xbarrier(); |
360 |
|
if (--suppress_int == 0 && pending_int) { |
361 |
raise_interrupt(); |
raise_interrupt(); |
362 |
} |
} |
363 |
} |
} |
364 |
#define INT_ON int_on() |
#define INT_ON int_on() |
365 |
static void |
static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void |
366 |
force_int_on(void) |
force_int_on(void) |
367 |
{ |
{ |
368 |
suppressint = 0; |
xbarrier(); |
369 |
if (intpending) |
suppress_int = 0; |
370 |
|
if (pending_int) |
371 |
raise_interrupt(); |
raise_interrupt(); |
372 |
} |
} |
373 |
#define FORCE_INT_ON force_int_on() |
#define FORCE_INT_ON force_int_on() |
374 |
|
|
375 |
#else /* !ASH_OPTIMIZE_FOR_SIZE */ |
#define SAVE_INT(v) ((v) = suppress_int) |
|
|
|
|
#define INT_ON do { \ |
|
|
xbarrier(); \ |
|
|
if (--suppressint == 0 && intpending) \ |
|
|
raise_interrupt(); \ |
|
|
} while (0) |
|
|
#define FORCE_INT_ON do { \ |
|
|
xbarrier(); \ |
|
|
suppressint = 0; \ |
|
|
if (intpending) \ |
|
|
raise_interrupt(); \ |
|
|
} while (0) |
|
|
#endif /* !ASH_OPTIMIZE_FOR_SIZE */ |
|
|
|
|
|
#define SAVE_INT(v) ((v) = suppressint) |
|
376 |
|
|
377 |
#define RESTORE_INT(v) do { \ |
#define RESTORE_INT(v) do { \ |
378 |
xbarrier(); \ |
xbarrier(); \ |
379 |
suppressint = (v); \ |
suppress_int = (v); \ |
380 |
if (suppressint == 0 && intpending) \ |
if (suppress_int == 0 && pending_int) \ |
381 |
raise_interrupt(); \ |
raise_interrupt(); \ |
382 |
} while (0) |
} while (0) |
383 |
|
|
|
/* |
|
|
* Ignore a signal. Only one usage site - in forkchild() |
|
|
*/ |
|
|
static void |
|
|
ignoresig(int signo) |
|
|
{ |
|
|
if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) { |
|
|
signal(signo, SIG_IGN); |
|
|
} |
|
|
sigmode[signo - 1] = S_HARD_IGN; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Signal handler. Only one usage site - in setsignal() |
|
|
*/ |
|
|
static void |
|
|
onsig(int signo) |
|
|
{ |
|
|
gotsig[signo - 1] = 1; |
|
|
|
|
|
if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) { |
|
|
if (!suppressint) { |
|
|
pendingsig = 0; |
|
|
raise_interrupt(); /* does not return */ |
|
|
} |
|
|
intpending = 1; |
|
|
} else { |
|
|
pendingsig = signo; |
|
|
} |
|
|
} |
|
|
|
|
384 |
|
|
385 |
/* ============ Stdout/stderr output */ |
/* ============ Stdout/stderr output */ |
386 |
|
|
396 |
flush_stdout_stderr(void) |
flush_stdout_stderr(void) |
397 |
{ |
{ |
398 |
INT_OFF; |
INT_OFF; |
399 |
fflush(stdout); |
fflush_all(); |
|
fflush(stderr); |
|
|
INT_ON; |
|
|
} |
|
|
|
|
|
static void |
|
|
flush_stderr(void) |
|
|
{ |
|
|
INT_OFF; |
|
|
fflush(stderr); |
|
400 |
INT_ON; |
INT_ON; |
401 |
} |
} |
402 |
|
|
449 |
out2str(const char *p) |
out2str(const char *p) |
450 |
{ |
{ |
451 |
outstr(p, stderr); |
outstr(p, stderr); |
452 |
flush_stderr(); |
flush_stdout_stderr(); |
453 |
} |
} |
454 |
|
|
455 |
|
|
456 |
/* ============ Parser structures */ |
/* ============ Parser structures */ |
457 |
|
|
458 |
/* control characters in argument strings */ |
/* control characters in argument strings */ |
459 |
#define CTLESC '\201' /* escape next character */ |
#define CTL_FIRST CTLESC |
460 |
#define CTLVAR '\202' /* variable defn */ |
#define CTLESC ((unsigned char)'\201') /* escape next character */ |
461 |
#define CTLENDVAR '\203' |
#define CTLVAR ((unsigned char)'\202') /* variable defn */ |
462 |
#define CTLBACKQ '\204' |
#define CTLENDVAR ((unsigned char)'\203') |
463 |
|
#define CTLBACKQ ((unsigned char)'\204') |
464 |
#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ |
#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ |
465 |
/* CTLBACKQ | CTLQUOTE == '\205' */ |
/* CTLBACKQ | CTLQUOTE == '\205' */ |
466 |
#define CTLARI '\206' /* arithmetic expression */ |
#define CTLARI ((unsigned char)'\206') /* arithmetic expression */ |
467 |
#define CTLENDARI '\207' |
#define CTLENDARI ((unsigned char)'\207') |
468 |
#define CTLQUOTEMARK '\210' |
#define CTLQUOTEMARK ((unsigned char)'\210') |
469 |
|
#define CTL_LAST CTLQUOTEMARK |
470 |
|
|
471 |
/* variable substitution byte (follows CTLVAR) */ |
/* variable substitution byte (follows CTLVAR) */ |
472 |
#define VSTYPE 0x0f /* type of variable substitution */ |
#define VSTYPE 0x0f /* type of variable substitution */ |
637 |
struct nnot nnot; |
struct nnot nnot; |
638 |
}; |
}; |
639 |
|
|
640 |
|
/* |
641 |
|
* NODE_EOF is returned by parsecmd when it encounters an end of file. |
642 |
|
* It must be distinct from NULL. |
643 |
|
*/ |
644 |
|
#define NODE_EOF ((union node *) -1L) |
645 |
|
|
646 |
struct nodelist { |
struct nodelist { |
647 |
struct nodelist *next; |
struct nodelist *next; |
648 |
union node *n; |
union node *n; |
682 |
if (DEBUG_PID) |
if (DEBUG_PID) |
683 |
fprintf(tracefile, "[%u] ", (int) getpid()); |
fprintf(tracefile, "[%u] ", (int) getpid()); |
684 |
if (DEBUG_SIG) |
if (DEBUG_SIG) |
685 |
fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pendingsig, intpending, suppressint); |
fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int); |
686 |
va_start(va, fmt); |
va_start(va, fmt); |
687 |
vfprintf(tracefile, fmt, va); |
vfprintf(tracefile, fmt, va); |
688 |
va_end(va); |
va_end(va); |
698 |
if (DEBUG_PID) |
if (DEBUG_PID) |
699 |
fprintf(tracefile, "[%u] ", (int) getpid()); |
fprintf(tracefile, "[%u] ", (int) getpid()); |
700 |
if (DEBUG_SIG) |
if (DEBUG_SIG) |
701 |
fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pendingsig, intpending, suppressint); |
fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int); |
702 |
vfprintf(tracefile, fmt, va); |
vfprintf(tracefile, fmt, va); |
703 |
} |
} |
704 |
|
|
720 |
return; |
return; |
721 |
putc('"', tracefile); |
putc('"', tracefile); |
722 |
for (p = s; *p; p++) { |
for (p = s; *p; p++) { |
723 |
switch (*p) { |
switch ((unsigned char)*p) { |
724 |
case '\n': c = 'n'; goto backslash; |
case '\n': c = 'n'; goto backslash; |
725 |
case '\t': c = 't'; goto backslash; |
case '\t': c = 't'; goto backslash; |
726 |
case '\r': c = 'r'; goto backslash; |
case '\r': c = 'r'; goto backslash; |
727 |
case '"': c = '"'; goto backslash; |
case '\"': c = '\"'; goto backslash; |
728 |
case '\\': c = '\\'; goto backslash; |
case '\\': c = '\\'; goto backslash; |
729 |
case CTLESC: c = 'e'; goto backslash; |
case CTLESC: c = 'e'; goto backslash; |
730 |
case CTLVAR: c = 'v'; goto backslash; |
case CTLVAR: c = 'v'; goto backslash; |
731 |
case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; |
case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; |
732 |
case CTLBACKQ: c = 'q'; goto backslash; |
case CTLBACKQ: c = 'q'; goto backslash; |
733 |
case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; |
case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; |
734 |
backslash: |
backslash: |
735 |
putc('\\', tracefile); |
putc('\\', tracefile); |
736 |
putc(c, tracefile); |
putc(c, tracefile); |
740 |
putc(*p, tracefile); |
putc(*p, tracefile); |
741 |
else { |
else { |
742 |
putc('\\', tracefile); |
putc('\\', tracefile); |
743 |
putc(*p >> 6 & 03, tracefile); |
putc((*p >> 6) & 03, tracefile); |
744 |
putc(*p >> 3 & 07, tracefile); |
putc((*p >> 3) & 07, tracefile); |
745 |
putc(*p & 07, tracefile); |
putc(*p & 07, tracefile); |
746 |
} |
} |
747 |
break; |
break; |
825 |
{ |
{ |
826 |
char *p; |
char *p; |
827 |
struct nodelist *bqlist; |
struct nodelist *bqlist; |
828 |
int subtype; |
unsigned char subtype; |
829 |
|
|
830 |
if (arg->type != NARG) { |
if (arg->type != NARG) { |
831 |
out1fmt("<node type %d>\n", arg->type); |
out1fmt("<node type %d>\n", arg->type); |
833 |
} |
} |
834 |
bqlist = arg->narg.backquote; |
bqlist = arg->narg.backquote; |
835 |
for (p = arg->narg.text; *p; p++) { |
for (p = arg->narg.text; *p; p++) { |
836 |
switch (*p) { |
switch ((unsigned char)*p) { |
837 |
case CTLESC: |
case CTLESC: |
838 |
putc(*++p, fp); |
putc(*++p, fp); |
839 |
break; |
break; |
957 |
return; |
return; |
958 |
|
|
959 |
indent(ind, pfx, fp); |
indent(ind, pfx, fp); |
960 |
|
|
961 |
|
if (n == NODE_EOF) { |
962 |
|
fputs("<EOF>", fp); |
963 |
|
return; |
964 |
|
} |
965 |
|
|
966 |
switch (n->type) { |
switch (n->type) { |
967 |
case NSEMI: |
case NSEMI: |
968 |
s = "; "; |
s = "; "; |
985 |
break; |
break; |
986 |
case NPIPE: |
case NPIPE: |
987 |
for (lp = n->npipe.cmdlist; lp; lp = lp->next) { |
for (lp = n->npipe.cmdlist; lp; lp = lp->next) { |
988 |
shcmd(lp->n, fp); |
shtree(lp->n, 0, NULL, fp); |
989 |
if (lp->next) |
if (lp->next) |
990 |
fputs(" | ", fp); |
fputs(" | ", fp); |
991 |
} |
} |
1006 |
showtree(union node *n) |
showtree(union node *n) |
1007 |
{ |
{ |
1008 |
trace_puts("showtree called\n"); |
trace_puts("showtree called\n"); |
1009 |
shtree(n, 1, NULL, stdout); |
shtree(n, 1, NULL, stderr); |
1010 |
} |
} |
1011 |
|
|
1012 |
#endif /* DEBUG */ |
#endif /* DEBUG */ |
1026 |
|
|
1027 |
struct strpush { |
struct strpush { |
1028 |
struct strpush *prev; /* preceding string on stack */ |
struct strpush *prev; /* preceding string on stack */ |
1029 |
char *prevstring; |
char *prev_string; |
1030 |
int prevnleft; |
int prev_left_in_line; |
1031 |
#if ENABLE_ASH_ALIAS |
#if ENABLE_ASH_ALIAS |
1032 |
struct alias *ap; /* if push was associated with an alias */ |
struct alias *ap; /* if push was associated with an alias */ |
1033 |
#endif |
#endif |
1038 |
struct parsefile *prev; /* preceding file on stack */ |
struct parsefile *prev; /* preceding file on stack */ |
1039 |
int linno; /* current line */ |
int linno; /* current line */ |
1040 |
int fd; /* file descriptor (or -1 if string) */ |
int fd; /* file descriptor (or -1 if string) */ |
1041 |
int nleft; /* number of chars left in this line */ |
int left_in_line; /* number of chars left in this line */ |
1042 |
int lleft; /* number of chars left in this buffer */ |
int left_in_buffer; /* number of chars left in this buffer past the line */ |
1043 |
char *nextc; /* next char in buffer */ |
char *next_to_pgetc; /* next char in buffer */ |
1044 |
char *buf; /* input buffer */ |
char *buf; /* input buffer */ |
1045 |
struct strpush *strpush; /* for pushing strings at this level */ |
struct strpush *strpush; /* for pushing strings at this level */ |
1046 |
struct strpush basestrpush; /* so pushing one is fast */ |
struct strpush basestrpush; /* so pushing one is fast */ |
1107 |
va_end(ap); |
va_end(ap); |
1108 |
} |
} |
1109 |
|
|
1110 |
|
static void raise_error_syntax(const char *) NORETURN; |
1111 |
|
static void |
1112 |
|
raise_error_syntax(const char *msg) |
1113 |
|
{ |
1114 |
|
ash_msg_and_raise_error("syntax error: %s", msg); |
1115 |
|
/* NOTREACHED */ |
1116 |
|
} |
1117 |
|
|
1118 |
static void ash_msg_and_raise(int, const char *, ...) NORETURN; |
static void ash_msg_and_raise(int, const char *, ...) NORETURN; |
1119 |
static void |
static void |
1120 |
ash_msg_and_raise(int cond, const char *msg, ...) |
ash_msg_and_raise(int cond, const char *msg, ...) |
1157 |
|
|
1158 |
/* ============ Memory allocation */ |
/* ============ Memory allocation */ |
1159 |
|
|
1160 |
|
#if 0 |
1161 |
|
/* I consider these wrappers nearly useless: |
1162 |
|
* ok, they return you to nearest exception handler, but |
1163 |
|
* how much memory do you leak in the process, making |
1164 |
|
* memory starvation worse? |
1165 |
|
*/ |
1166 |
|
static void * |
1167 |
|
ckrealloc(void * p, size_t nbytes) |
1168 |
|
{ |
1169 |
|
p = realloc(p, nbytes); |
1170 |
|
if (!p) |
1171 |
|
ash_msg_and_raise_error(bb_msg_memory_exhausted); |
1172 |
|
return p; |
1173 |
|
} |
1174 |
|
|
1175 |
|
static void * |
1176 |
|
ckmalloc(size_t nbytes) |
1177 |
|
{ |
1178 |
|
return ckrealloc(NULL, nbytes); |
1179 |
|
} |
1180 |
|
|
1181 |
|
static void * |
1182 |
|
ckzalloc(size_t nbytes) |
1183 |
|
{ |
1184 |
|
return memset(ckmalloc(nbytes), 0, nbytes); |
1185 |
|
} |
1186 |
|
|
1187 |
|
static char * |
1188 |
|
ckstrdup(const char *s) |
1189 |
|
{ |
1190 |
|
char *p = strdup(s); |
1191 |
|
if (!p) |
1192 |
|
ash_msg_and_raise_error(bb_msg_memory_exhausted); |
1193 |
|
return p; |
1194 |
|
} |
1195 |
|
#else |
1196 |
|
/* Using bbox equivalents. They exit if out of memory */ |
1197 |
|
# define ckrealloc xrealloc |
1198 |
|
# define ckmalloc xmalloc |
1199 |
|
# define ckzalloc xzalloc |
1200 |
|
# define ckstrdup xstrdup |
1201 |
|
#endif |
1202 |
|
|
1203 |
/* |
/* |
1204 |
* It appears that grabstackstr() will barf with such alignments |
* It appears that grabstackstr() will barf with such alignments |
1205 |
* because stalloc() will return a string allocated in a new stackblock. |
* because stalloc() will return a string allocated in a new stackblock. |
1209 |
/* Most machines require the value returned from malloc to be aligned |
/* Most machines require the value returned from malloc to be aligned |
1210 |
* in some way. The following macro will get this right |
* in some way. The following macro will get this right |
1211 |
* on many machines. */ |
* on many machines. */ |
1212 |
SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1, |
SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1, |
1213 |
/* Minimum size of a block */ |
/* Minimum size of a block */ |
1214 |
MINSIZE = SHELL_ALIGN(504), |
MINSIZE = SHELL_ALIGN(504), |
1215 |
}; |
}; |
1255 |
herefd = -1; \ |
herefd = -1; \ |
1256 |
} while (0) |
} while (0) |
1257 |
|
|
1258 |
|
|
1259 |
#define stackblock() ((void *)g_stacknxt) |
#define stackblock() ((void *)g_stacknxt) |
1260 |
#define stackblocksize() g_stacknleft |
#define stackblocksize() g_stacknleft |
1261 |
|
|
|
|
|
|
static void * |
|
|
ckrealloc(void * p, size_t nbytes) |
|
|
{ |
|
|
p = realloc(p, nbytes); |
|
|
if (!p) |
|
|
ash_msg_and_raise_error(bb_msg_memory_exhausted); |
|
|
return p; |
|
|
} |
|
|
|
|
|
static void * |
|
|
ckmalloc(size_t nbytes) |
|
|
{ |
|
|
return ckrealloc(NULL, nbytes); |
|
|
} |
|
|
|
|
|
static void * |
|
|
ckzalloc(size_t nbytes) |
|
|
{ |
|
|
return memset(ckmalloc(nbytes), 0, nbytes); |
|
|
} |
|
|
|
|
|
/* |
|
|
* Make a copy of a string in safe storage. |
|
|
*/ |
|
|
static char * |
|
|
ckstrdup(const char *s) |
|
|
{ |
|
|
char *p = strdup(s); |
|
|
if (!p) |
|
|
ash_msg_and_raise_error(bb_msg_memory_exhausted); |
|
|
return p; |
|
|
} |
|
|
|
|
1262 |
/* |
/* |
1263 |
* Parse trees for commands are allocated in lifo order, so we use a stack |
* Parse trees for commands are allocated in lifo order, so we use a stack |
1264 |
* to make this more efficient, and also to avoid all sorts of exception |
* to make this more efficient, and also to avoid all sorts of exception |
1563 |
number(const char *s) |
number(const char *s) |
1564 |
{ |
{ |
1565 |
if (!is_number(s)) |
if (!is_number(s)) |
1566 |
ash_msg_and_raise_error(illnum, s); |
ash_msg_and_raise_error(msg_illnum, s); |
1567 |
return atoi(s); |
return atoi(s); |
1568 |
} |
} |
1569 |
|
|
1593 |
|
|
1594 |
STADJUST(q - p, p); |
STADJUST(q - p, p); |
1595 |
|
|
1596 |
len = strspn(s, "'"); |
if (*s != '\'') |
|
if (!len) |
|
1597 |
break; |
break; |
1598 |
|
len = 0; |
1599 |
|
do len++; while (*++s == '\''); |
1600 |
|
|
1601 |
q = p = makestrspace(len + 3, p); |
q = p = makestrspace(len + 3, p); |
1602 |
|
|
1603 |
*q++ = '"'; |
*q++ = '"'; |
1604 |
q = (char *)memcpy(q, s, len) + len; |
q = (char *)memcpy(q, s - len, len) + len; |
1605 |
*q++ = '"'; |
*q++ = '"'; |
|
s += len; |
|
1606 |
|
|
1607 |
STADJUST(q - p, p); |
STADJUST(q - p, p); |
1608 |
} while (*s); |
} while (*s); |
1609 |
|
|
1610 |
USTPUTC(0, p); |
USTPUTC('\0', p); |
1611 |
|
|
1612 |
return stackblock(); |
return stackblock(); |
1613 |
} |
} |
1705 |
} |
} |
1706 |
|
|
1707 |
#if ENABLE_ASH_GETOPTS |
#if ENABLE_ASH_GETOPTS |
1708 |
static void getoptsreset(const char *value); |
static void FAST_FUNC getoptsreset(const char *value); |
1709 |
#endif |
#endif |
1710 |
|
|
1711 |
struct var { |
struct var { |
1712 |
struct var *next; /* next entry in hash list */ |
struct var *next; /* next entry in hash list */ |
1713 |
int flags; /* flags are defined above */ |
int flags; /* flags are defined above */ |
1714 |
const char *text; /* name=value */ |
const char *text; /* name=value */ |
1715 |
void (*func)(const char *); /* function to be called when */ |
void (*func)(const char *) FAST_FUNC; /* function to be called when */ |
1716 |
/* the variable gets set/unset */ |
/* the variable gets set/unset */ |
1717 |
}; |
}; |
1718 |
|
|
1739 |
# define VDYNAMIC 0 |
# define VDYNAMIC 0 |
1740 |
#endif |
#endif |
1741 |
|
|
|
#ifdef IFS_BROKEN |
|
|
static const char defifsvar[] ALIGN1 = "IFS= \t\n"; |
|
|
#define defifs (defifsvar + 4) |
|
|
#else |
|
|
static const char defifs[] ALIGN1 = " \t\n"; |
|
|
#endif |
|
|
|
|
1742 |
|
|
1743 |
/* Need to be before varinit_data[] */ |
/* Need to be before varinit_data[] */ |
1744 |
#if ENABLE_LOCALE_SUPPORT |
#if ENABLE_LOCALE_SUPPORT |
1745 |
static void |
static void FAST_FUNC |
1746 |
change_lc_all(const char *value) |
change_lc_all(const char *value) |
1747 |
{ |
{ |
1748 |
if (value && *value != '\0') |
if (value && *value != '\0') |
1749 |
setlocale(LC_ALL, value); |
setlocale(LC_ALL, value); |
1750 |
} |
} |
1751 |
static void |
static void FAST_FUNC |
1752 |
change_lc_ctype(const char *value) |
change_lc_ctype(const char *value) |
1753 |
{ |
{ |
1754 |
if (value && *value != '\0') |
if (value && *value != '\0') |
1757 |
#endif |
#endif |
1758 |
#if ENABLE_ASH_MAIL |
#if ENABLE_ASH_MAIL |
1759 |
static void chkmail(void); |
static void chkmail(void); |
1760 |
static void changemail(const char *); |
static void changemail(const char *) FAST_FUNC; |
1761 |
#endif |
#endif |
1762 |
static void changepath(const char *); |
static void changepath(const char *) FAST_FUNC; |
1763 |
#if ENABLE_ASH_RANDOM_SUPPORT |
#if ENABLE_ASH_RANDOM_SUPPORT |
1764 |
static void change_random(const char *); |
static void change_random(const char *) FAST_FUNC; |
1765 |
#endif |
#endif |
1766 |
|
|
1767 |
static const struct { |
static const struct { |
1768 |
int flags; |
int flags; |
1769 |
const char *text; |
const char *text; |
1770 |
void (*func)(const char *); |
void (*func)(const char *) FAST_FUNC; |
1771 |
} varinit_data[] = { |
} varinit_data[] = { |
|
#ifdef IFS_BROKEN |
|
1772 |
{ VSTRFIXED|VTEXTFIXED , defifsvar , NULL }, |
{ VSTRFIXED|VTEXTFIXED , defifsvar , NULL }, |
|
#else |
|
|
{ VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL }, |
|
|
#endif |
|
1773 |
#if ENABLE_ASH_MAIL |
#if ENABLE_ASH_MAIL |
1774 |
{ VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail }, |
{ VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail }, |
1775 |
{ VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail }, |
{ VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail }, |
1869 |
#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) |
#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) |
1870 |
|
|
1871 |
#if ENABLE_ASH_GETOPTS |
#if ENABLE_ASH_GETOPTS |
1872 |
static void |
static void FAST_FUNC |
1873 |
getoptsreset(const char *value) |
getoptsreset(const char *value) |
1874 |
{ |
{ |
1875 |
shellparam.optind = number(value); |
shellparam.optind = number(value); |
1881 |
* Return of a legal variable name (a letter or underscore followed by zero or |
* Return of a legal variable name (a letter or underscore followed by zero or |
1882 |
* more letters, underscores, and digits). |
* more letters, underscores, and digits). |
1883 |
*/ |
*/ |
1884 |
static char * |
static char* FAST_FUNC |
1885 |
endofname(const char *name) |
endofname(const char *name) |
1886 |
{ |
{ |
1887 |
char *p; |
char *p; |
1988 |
/* |
/* |
1989 |
* Find the value of a variable. Returns NULL if not set. |
* Find the value of a variable. Returns NULL if not set. |
1990 |
*/ |
*/ |
1991 |
static char * |
static const char* FAST_FUNC |
1992 |
lookupvar(const char *name) |
lookupvar(const char *name) |
1993 |
{ |
{ |
1994 |
struct var *v; |
struct var *v; |
2014 |
/* |
/* |
2015 |
* Search the environment of a builtin command. |
* Search the environment of a builtin command. |
2016 |
*/ |
*/ |
2017 |
static char * |
static const char * |
2018 |
bltinlookup(const char *name) |
bltinlookup(const char *name) |
2019 |
{ |
{ |
2020 |
struct strlist *sp; |
struct strlist *sp; |
2111 |
INT_ON; |
INT_ON; |
2112 |
} |
} |
2113 |
|
|
2114 |
|
static void FAST_FUNC |
2115 |
|
setvar2(const char *name, const char *val) |
2116 |
|
{ |
2117 |
|
setvar(name, val, 0); |
2118 |
|
} |
2119 |
|
|
2120 |
#if ENABLE_ASH_GETOPTS |
#if ENABLE_ASH_GETOPTS |
2121 |
/* |
/* |
2122 |
* Safe version of setvar, returns 1 on success 0 on failure. |
* Safe version of setvar, returns 1 on success 0 on failure. |
2238 |
/* ============ Path search helper |
/* ============ Path search helper |
2239 |
* |
* |
2240 |
* The variable path (passed by reference) should be set to the start |
* The variable path (passed by reference) should be set to the start |
2241 |
* of the path before the first call; padvance will update |
* of the path before the first call; path_advance will update |
2242 |
* this value as it proceeds. Successive calls to padvance will return |
* this value as it proceeds. Successive calls to path_advance will return |
2243 |
* the possible path expansions in sequence. If an option (indicated by |
* the possible path expansions in sequence. If an option (indicated by |
2244 |
* a percent sign) appears in the path entry then the global variable |
* a percent sign) appears in the path entry then the global variable |
2245 |
* pathopt will be set to point to it; otherwise pathopt will be set to |
* pathopt will be set to point to it; otherwise pathopt will be set to |
2246 |
* NULL. |
* NULL. |
2247 |
*/ |
*/ |
2248 |
static const char *pathopt; /* set by padvance */ |
static const char *pathopt; /* set by path_advance */ |
2249 |
|
|
2250 |
static char * |
static char * |
2251 |
padvance(const char **path, const char *name) |
path_advance(const char **path, const char *name) |
2252 |
{ |
{ |
2253 |
const char *p; |
const char *p; |
2254 |
char *q; |
char *q; |
2353 |
#define CD_PHYSICAL 1 |
#define CD_PHYSICAL 1 |
2354 |
#define CD_PRINT 2 |
#define CD_PRINT 2 |
2355 |
|
|
|
static int docd(const char *, int); |
|
|
|
|
2356 |
static int |
static int |
2357 |
cdopt(void) |
cdopt(void) |
2358 |
{ |
{ |
2360 |
int i, j; |
int i, j; |
2361 |
|
|
2362 |
j = 'L'; |
j = 'L'; |
2363 |
while ((i = nextopt("LP"))) { |
while ((i = nextopt("LP")) != '\0') { |
2364 |
if (i != j) { |
if (i != j) { |
2365 |
flags ^= CD_PHYSICAL; |
flags ^= CD_PHYSICAL; |
2366 |
j = i; |
j = i; |
2504 |
return err; |
return err; |
2505 |
} |
} |
2506 |
|
|
2507 |
static int |
static int FAST_FUNC |
2508 |
cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
2509 |
{ |
{ |
2510 |
const char *dest; |
const char *dest; |
2550 |
} |
} |
2551 |
do { |
do { |
2552 |
c = *path; |
c = *path; |
2553 |
p = padvance(&path, dest); |
p = path_advance(&path, dest); |
2554 |
if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { |
if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { |
2555 |
if (c && c != ':') |
if (c && c != ':') |
2556 |
flags |= CD_PRINT; |
flags |= CD_PRINT; |
2568 |
return 0; |
return 0; |
2569 |
} |
} |
2570 |
|
|
2571 |
static int |
static int FAST_FUNC |
2572 |
pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
2573 |
{ |
{ |
2574 |
int flags; |
int flags; |
2609 |
#define CSPCL 13 /* these terminate a word */ |
#define CSPCL 13 /* these terminate a word */ |
2610 |
#define CIGN 14 /* character should be ignored */ |
#define CIGN 14 /* character should be ignored */ |
2611 |
|
|
2612 |
|
#define PEOF 256 |
2613 |
#if ENABLE_ASH_ALIAS |
#if ENABLE_ASH_ALIAS |
2614 |
#define SYNBASE 130 |
# define PEOA 257 |
|
#define PEOF -130 |
|
|
#define PEOA -129 |
|
|
#define PEOA_OR_PEOF PEOA |
|
|
#else |
|
|
#define SYNBASE 129 |
|
|
#define PEOF -129 |
|
|
#define PEOA_OR_PEOF PEOF |
|
2615 |
#endif |
#endif |
2616 |
|
|
2617 |
/* number syntax index */ |
#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE |
|
#define BASESYNTAX 0 /* not in quotes */ |
|
|
#define DQSYNTAX 1 /* in double quotes */ |
|
|
#define SQSYNTAX 2 /* in single quotes */ |
|
|
#define ARISYNTAX 3 /* in arithmetic */ |
|
|
#define PSSYNTAX 4 /* prompt */ |
|
2618 |
|
|
2619 |
#if ENABLE_ASH_OPTIMIZE_FOR_SIZE |
#if ENABLE_SH_MATH_SUPPORT |
2620 |
#define USE_SIT_FUNCTION |
# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12)) |
2621 |
|
#else |
2622 |
|
# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8)) |
2623 |
#endif |
#endif |
2624 |
|
static const uint16_t S_I_T[] = { |
|
#if ENABLE_ASH_MATH_SUPPORT |
|
|
static const char S_I_T[][4] = { |
|
2625 |
#if ENABLE_ASH_ALIAS |
#if ENABLE_ASH_ALIAS |
2626 |
{ CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */ |
SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */ |
2627 |
#endif |
#endif |
2628 |
{ CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */ |
SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */ |
2629 |
{ CNL, CNL, CNL, CNL }, /* 2, \n */ |
SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */ |
2630 |
{ CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */ |
SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */ |
2631 |
{ CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */ |
SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */ |
2632 |
{ CVAR, CVAR, CWORD, CVAR }, /* 5, $ */ |
SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */ |
2633 |
{ CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */ |
SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */ |
2634 |
{ CSPCL, CWORD, CWORD, CLP }, /* 7, ( */ |
SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */ |
2635 |
{ CSPCL, CWORD, CWORD, CRP }, /* 8, ) */ |
SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */ |
2636 |
{ CBACK, CBACK, CCTL, CBACK }, /* 9, \ */ |
SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */ |
2637 |
{ CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */ |
SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */ |
2638 |
{ CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */ |
SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */ |
2639 |
#ifndef USE_SIT_FUNCTION |
#if !USE_SIT_FUNCTION |
2640 |
{ CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */ |
SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */ |
2641 |
{ CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */ |
SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */ |
2642 |
{ CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */ |
SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */ |
2643 |
#endif |
#endif |
2644 |
|
#undef SIT_ITEM |
2645 |
}; |
}; |
2646 |
#else |
/* Constants below must match table above */ |
2647 |
static const char S_I_T[][3] = { |
enum { |
2648 |
#if ENABLE_ASH_ALIAS |
#if ENABLE_ASH_ALIAS |
2649 |
{ CSPCL, CIGN, CIGN }, /* 0, PEOA */ |
CSPCL_CIGN_CIGN_CIGN , /* 0 */ |
|
#endif |
|
|
{ CSPCL, CWORD, CWORD }, /* 1, ' ' */ |
|
|
{ CNL, CNL, CNL }, /* 2, \n */ |
|
|
{ CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */ |
|
|
{ CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */ |
|
|
{ CVAR, CVAR, CWORD }, /* 5, $ */ |
|
|
{ CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */ |
|
|
{ CSPCL, CWORD, CWORD }, /* 7, ( */ |
|
|
{ CSPCL, CWORD, CWORD }, /* 8, ) */ |
|
|
{ CBACK, CBACK, CCTL }, /* 9, \ */ |
|
|
{ CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */ |
|
|
{ CENDVAR, CENDVAR, CWORD }, /* 11, } */ |
|
|
#ifndef USE_SIT_FUNCTION |
|
|
{ CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */ |
|
|
{ CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */ |
|
|
{ CCTL, CCTL, CCTL } /* 14, CTLESC ... */ |
|
2650 |
#endif |
#endif |
2651 |
|
CSPCL_CWORD_CWORD_CWORD , /* 1 */ |
2652 |
|
CNL_CNL_CNL_CNL , /* 2 */ |
2653 |
|
CWORD_CCTL_CCTL_CWORD , /* 3 */ |
2654 |
|
CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */ |
2655 |
|
CVAR_CVAR_CWORD_CVAR , /* 5 */ |
2656 |
|
CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */ |
2657 |
|
CSPCL_CWORD_CWORD_CLP , /* 7 */ |
2658 |
|
CSPCL_CWORD_CWORD_CRP , /* 8 */ |
2659 |
|
CBACK_CBACK_CCTL_CBACK , /* 9 */ |
2660 |
|
CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */ |
2661 |
|
CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */ |
2662 |
|
CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */ |
2663 |
|
CWORD_CWORD_CWORD_CWORD , /* 13 */ |
2664 |
|
CCTL_CCTL_CCTL_CCTL , /* 14 */ |
2665 |
}; |
}; |
|
#endif /* ASH_MATH_SUPPORT */ |
|
2666 |
|
|
2667 |
#ifdef USE_SIT_FUNCTION |
/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF, |
2668 |
|
* caller must ensure proper cast on it if c is *char_ptr! |
2669 |
|
*/ |
2670 |
|
/* Values for syntax param */ |
2671 |
|
#define BASESYNTAX 0 /* not in quotes */ |
2672 |
|
#define DQSYNTAX 1 /* in double quotes */ |
2673 |
|
#define SQSYNTAX 2 /* in single quotes */ |
2674 |
|
#define ARISYNTAX 3 /* in arithmetic */ |
2675 |
|
#define PSSYNTAX 4 /* prompt. never passed to SIT() */ |
2676 |
|
|
2677 |
|
#if USE_SIT_FUNCTION |
2678 |
|
|
2679 |
static int |
static int |
2680 |
SIT(int c, int syntax) |
SIT(int c, int syntax) |
2681 |
{ |
{ |
2682 |
static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~"; |
static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~"; |
2683 |
#if ENABLE_ASH_ALIAS |
# if ENABLE_ASH_ALIAS |
2684 |
static const char syntax_index_table[] ALIGN1 = { |
static const uint8_t syntax_index_table[] ALIGN1 = { |
2685 |
1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */ |
1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */ |
2686 |
7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */ |
7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */ |
2687 |
3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */ |
3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */ |
2688 |
11, 3 /* "}~" */ |
11, 3 /* "}~" */ |
2689 |
}; |
}; |
2690 |
#else |
# else |
2691 |
static const char syntax_index_table[] ALIGN1 = { |
static const uint8_t syntax_index_table[] ALIGN1 = { |
2692 |
0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */ |
0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */ |
2693 |
6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */ |
6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */ |
2694 |
2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */ |
2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */ |
2695 |
10, 2 /* "}~" */ |
10, 2 /* "}~" */ |
2696 |
}; |
}; |
2697 |
#endif |
# endif |
2698 |
const char *s; |
const char *s; |
2699 |
int indx; |
int indx; |
2700 |
|
|
2701 |
if (c == PEOF) /* 2^8+2 */ |
if (c == PEOF) |
2702 |
return CENDFILE; |
return CENDFILE; |
2703 |
#if ENABLE_ASH_ALIAS |
# if ENABLE_ASH_ALIAS |
2704 |
if (c == PEOA) /* 2^8+1 */ |
if (c == PEOA) |
2705 |
indx = 0; |
indx = 0; |
2706 |
else |
else |
2707 |
#endif |
# endif |
2708 |
|
{ |
2709 |
if ((unsigned char)c >= (unsigned char)(CTLESC) |
/* Cast is purely for paranoia here, |
2710 |
&& (unsigned char)c <= (unsigned char)(CTLQUOTEMARK) |
* just in case someone passed signed char to us */ |
2711 |
) { |
if ((unsigned char)c >= CTL_FIRST |
2712 |
return CCTL; |
&& (unsigned char)c <= CTL_LAST |
2713 |
} else { |
) { |
2714 |
|
return CCTL; |
2715 |
|
} |
2716 |
s = strchrnul(spec_symbls, c); |
s = strchrnul(spec_symbls, c); |
2717 |
if (*s == '\0') |
if (*s == '\0') |
2718 |
return CWORD; |
return CWORD; |
2719 |
indx = syntax_index_table[s - spec_symbls]; |
indx = syntax_index_table[s - spec_symbls]; |
2720 |
} |
} |
2721 |
return S_I_T[indx][syntax]; |
return (S_I_T[indx] >> (syntax*4)) & 0xf; |
2722 |
} |
} |
2723 |
|
|
2724 |
#else /* !USE_SIT_FUNCTION */ |
#else /* !USE_SIT_FUNCTION */ |
2725 |
|
|
2726 |
#if ENABLE_ASH_ALIAS |
static const uint8_t syntax_index_table[] = { |
|
#define CSPCL_CIGN_CIGN_CIGN 0 |
|
|
#define CSPCL_CWORD_CWORD_CWORD 1 |
|
|
#define CNL_CNL_CNL_CNL 2 |
|
|
#define CWORD_CCTL_CCTL_CWORD 3 |
|
|
#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4 |
|
|
#define CVAR_CVAR_CWORD_CVAR 5 |
|
|
#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6 |
|
|
#define CSPCL_CWORD_CWORD_CLP 7 |
|
|
#define CSPCL_CWORD_CWORD_CRP 8 |
|
|
#define CBACK_CBACK_CCTL_CBACK 9 |
|
|
#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10 |
|
|
#define CENDVAR_CENDVAR_CWORD_CENDVAR 11 |
|
|
#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12 |
|
|
#define CWORD_CWORD_CWORD_CWORD 13 |
|
|
#define CCTL_CCTL_CCTL_CCTL 14 |
|
|
#else |
|
|
#define CSPCL_CWORD_CWORD_CWORD 0 |
|
|
#define CNL_CNL_CNL_CNL 1 |
|
|
#define CWORD_CCTL_CCTL_CWORD 2 |
|
|
#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3 |
|
|
#define CVAR_CVAR_CWORD_CVAR 4 |
|
|
#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5 |
|
|
#define CSPCL_CWORD_CWORD_CLP 6 |
|
|
#define CSPCL_CWORD_CWORD_CRP 7 |
|
|
#define CBACK_CBACK_CCTL_CBACK 8 |
|
|
#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9 |
|
|
#define CENDVAR_CENDVAR_CWORD_CENDVAR 10 |
|
|
#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11 |
|
|
#define CWORD_CWORD_CWORD_CWORD 12 |
|
|
#define CCTL_CCTL_CCTL_CCTL 13 |
|
|
#endif |
|
|
|
|
|
static const char syntax_index_table[258] = { |
|
2727 |
/* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */ |
/* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */ |
2728 |
/* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE, |
/* 0 */ CWORD_CWORD_CWORD_CWORD, |
2729 |
#if ENABLE_ASH_ALIAS |
/* 1 */ CWORD_CWORD_CWORD_CWORD, |
2730 |
/* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN, |
/* 2 */ CWORD_CWORD_CWORD_CWORD, |
2731 |
#endif |
/* 3 */ CWORD_CWORD_CWORD_CWORD, |
2732 |
/* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD, |
/* 4 */ CWORD_CWORD_CWORD_CWORD, |
2733 |
/* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL, |
/* 5 */ CWORD_CWORD_CWORD_CWORD, |
2734 |
/* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL, |
/* 6 */ CWORD_CWORD_CWORD_CWORD, |
2735 |
/* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL, |
/* 7 */ CWORD_CWORD_CWORD_CWORD, |
2736 |
/* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL, |
/* 8 */ CWORD_CWORD_CWORD_CWORD, |
2737 |
/* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL, |
/* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD, |
2738 |
/* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL, |
/* 10 "\n" */ CNL_CNL_CNL_CNL, |
2739 |
/* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL, |
/* 11 */ CWORD_CWORD_CWORD_CWORD, |
2740 |
/* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL, |
/* 12 */ CWORD_CWORD_CWORD_CWORD, |
2741 |
/* 11 -119 */ CWORD_CWORD_CWORD_CWORD, |
/* 13 */ CWORD_CWORD_CWORD_CWORD, |
2742 |
/* 12 -118 */ CWORD_CWORD_CWORD_CWORD, |
/* 14 */ CWORD_CWORD_CWORD_CWORD, |
2743 |
/* 13 -117 */ CWORD_CWORD_CWORD_CWORD, |
/* 15 */ CWORD_CWORD_CWORD_CWORD, |
2744 |
/* 14 -116 */ CWORD_CWORD_CWORD_CWORD, |
/* 16 */ CWORD_CWORD_CWORD_CWORD, |
2745 |
/* 15 -115 */ CWORD_CWORD_CWORD_CWORD, |
/* 17 */ CWORD_CWORD_CWORD_CWORD, |
2746 |
/* 16 -114 */ CWORD_CWORD_CWORD_CWORD, |
/* 18 */ CWORD_CWORD_CWORD_CWORD, |
2747 |
/* 17 -113 */ CWORD_CWORD_CWORD_CWORD, |
/* 19 */ CWORD_CWORD_CWORD_CWORD, |
2748 |
/* 18 -112 */ CWORD_CWORD_CWORD_CWORD, |
/* 20 */ CWORD_CWORD_CWORD_CWORD, |
2749 |
/* 19 -111 */ CWORD_CWORD_CWORD_CWORD, |
/* 21 */ CWORD_CWORD_CWORD_CWORD, |
2750 |
/* 20 -110 */ CWORD_CWORD_CWORD_CWORD, |
/* 22 */ CWORD_CWORD_CWORD_CWORD, |
2751 |
/* 21 -109 */ CWORD_CWORD_CWORD_CWORD, |
/* 23 */ CWORD_CWORD_CWORD_CWORD, |
2752 |
/* 22 -108 */ CWORD_CWORD_CWORD_CWORD, |
/* 24 */ CWORD_CWORD_CWORD_CWORD, |
2753 |
/* 23 -107 */ CWORD_CWORD_CWORD_CWORD, |
/* 25 */ CWORD_CWORD_CWORD_CWORD, |
2754 |
/* 24 -106 */ CWORD_CWORD_CWORD_CWORD, |
/* 26 */ CWORD_CWORD_CWORD_CWORD, |
2755 |
/* 25 -105 */ CWORD_CWORD_CWORD_CWORD, |
/* 27 */ CWORD_CWORD_CWORD_CWORD, |
2756 |
/* 26 -104 */ CWORD_CWORD_CWORD_CWORD, |
/* 28 */ CWORD_CWORD_CWORD_CWORD, |
2757 |
/* 27 -103 */ CWORD_CWORD_CWORD_CWORD, |
/* 29 */ CWORD_CWORD_CWORD_CWORD, |
2758 |
/* 28 -102 */ CWORD_CWORD_CWORD_CWORD, |
/* 30 */ CWORD_CWORD_CWORD_CWORD, |
2759 |
/* 29 -101 */ CWORD_CWORD_CWORD_CWORD, |
/* 31 */ CWORD_CWORD_CWORD_CWORD, |
2760 |
/* 30 -100 */ CWORD_CWORD_CWORD_CWORD, |
/* 32 " " */ CSPCL_CWORD_CWORD_CWORD, |
2761 |
/* 31 -99 */ CWORD_CWORD_CWORD_CWORD, |
/* 33 "!" */ CWORD_CCTL_CCTL_CWORD, |
2762 |
/* 32 -98 */ CWORD_CWORD_CWORD_CWORD, |
/* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD, |
2763 |
/* 33 -97 */ CWORD_CWORD_CWORD_CWORD, |
/* 35 "#" */ CWORD_CWORD_CWORD_CWORD, |
2764 |
/* 34 -96 */ CWORD_CWORD_CWORD_CWORD, |
/* 36 "$" */ CVAR_CVAR_CWORD_CVAR, |
2765 |
/* 35 -95 */ CWORD_CWORD_CWORD_CWORD, |
/* 37 "%" */ CWORD_CWORD_CWORD_CWORD, |
2766 |
/* 36 -94 */ CWORD_CWORD_CWORD_CWORD, |
/* 38 "&" */ CSPCL_CWORD_CWORD_CWORD, |
2767 |
/* 37 -93 */ CWORD_CWORD_CWORD_CWORD, |
/* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD, |
2768 |
/* 38 -92 */ CWORD_CWORD_CWORD_CWORD, |
/* 40 "(" */ CSPCL_CWORD_CWORD_CLP, |
2769 |
/* 39 -91 */ CWORD_CWORD_CWORD_CWORD, |
/* 41 ")" */ CSPCL_CWORD_CWORD_CRP, |
2770 |
/* 40 -90 */ CWORD_CWORD_CWORD_CWORD, |
/* 42 "*" */ CWORD_CCTL_CCTL_CWORD, |
2771 |
/* 41 -89 */ CWORD_CWORD_CWORD_CWORD, |
/* 43 "+" */ CWORD_CWORD_CWORD_CWORD, |
2772 |
/* 42 -88 */ CWORD_CWORD_CWORD_CWORD, |
/* 44 "," */ CWORD_CWORD_CWORD_CWORD, |
2773 |
/* 43 -87 */ CWORD_CWORD_CWORD_CWORD, |
/* 45 "-" */ CWORD_CCTL_CCTL_CWORD, |
2774 |
/* 44 -86 */ CWORD_CWORD_CWORD_CWORD, |
/* 46 "." */ CWORD_CWORD_CWORD_CWORD, |
2775 |
/* 45 -85 */ CWORD_CWORD_CWORD_CWORD, |
/* 47 "/" */ CWORD_CCTL_CCTL_CWORD, |
2776 |
/* 46 -84 */ CWORD_CWORD_CWORD_CWORD, |
/* 48 "0" */ CWORD_CWORD_CWORD_CWORD, |
2777 |
/* 47 -83 */ CWORD_CWORD_CWORD_CWORD, |
/* 49 "1" */ CWORD_CWORD_CWORD_CWORD, |
2778 |
/* 48 -82 */ CWORD_CWORD_CWORD_CWORD, |
/* 50 "2" */ CWORD_CWORD_CWORD_CWORD, |
2779 |
/* 49 -81 */ CWORD_CWORD_CWORD_CWORD, |
/* 51 "3" */ CWORD_CWORD_CWORD_CWORD, |
2780 |
/* 50 -80 */ CWORD_CWORD_CWORD_CWORD, |
/* 52 "4" */ CWORD_CWORD_CWORD_CWORD, |
2781 |
/* 51 -79 */ CWORD_CWORD_CWORD_CWORD, |
/* 53 "5" */ CWORD_CWORD_CWORD_CWORD, |
2782 |
/* 52 -78 */ CWORD_CWORD_CWORD_CWORD, |
/* 54 "6" */ CWORD_CWORD_CWORD_CWORD, |
2783 |
/* 53 -77 */ CWORD_CWORD_CWORD_CWORD, |
/* 55 "7" */ CWORD_CWORD_CWORD_CWORD, |
2784 |
/* 54 -76 */ CWORD_CWORD_CWORD_CWORD, |
/* 56 "8" */ CWORD_CWORD_CWORD_CWORD, |
2785 |
/* 55 -75 */ CWORD_CWORD_CWORD_CWORD, |
/* 57 "9" */ CWORD_CWORD_CWORD_CWORD, |
2786 |
/* 56 -74 */ CWORD_CWORD_CWORD_CWORD, |
/* 58 ":" */ CWORD_CCTL_CCTL_CWORD, |
2787 |
/* 57 -73 */ CWORD_CWORD_CWORD_CWORD, |
/* 59 ";" */ CSPCL_CWORD_CWORD_CWORD, |
2788 |
/* 58 -72 */ CWORD_CWORD_CWORD_CWORD, |
/* 60 "<" */ CSPCL_CWORD_CWORD_CWORD, |
2789 |
/* 59 -71 */ CWORD_CWORD_CWORD_CWORD, |
/* 61 "=" */ CWORD_CCTL_CCTL_CWORD, |
2790 |
/* 60 -70 */ CWORD_CWORD_CWORD_CWORD, |
/* 62 ">" */ CSPCL_CWORD_CWORD_CWORD, |
2791 |
/* 61 -69 */ CWORD_CWORD_CWORD_CWORD, |
/* 63 "?" */ CWORD_CCTL_CCTL_CWORD, |
2792 |
/* 62 -68 */ CWORD_CWORD_CWORD_CWORD, |
/* 64 "@" */ CWORD_CWORD_CWORD_CWORD, |
2793 |
/* 63 -67 */ CWORD_CWORD_CWORD_CWORD, |
/* 65 "A" */ CWORD_CWORD_CWORD_CWORD, |
2794 |
/* 64 -66 */ CWORD_CWORD_CWORD_CWORD, |
/* 66 "B" */ CWORD_CWORD_CWORD_CWORD, |
2795 |
/* 65 -65 */ CWORD_CWORD_CWORD_CWORD, |
/* 67 "C" */ CWORD_CWORD_CWORD_CWORD, |
2796 |
/* 66 -64 */ CWORD_CWORD_CWORD_CWORD, |
/* 68 "D" */ CWORD_CWORD_CWORD_CWORD, |
2797 |
/* 67 -63 */ CWORD_CWORD_CWORD_CWORD, |
/* 69 "E" */ CWORD_CWORD_CWORD_CWORD, |
2798 |
/* 68 -62 */ CWORD_CWORD_CWORD_CWORD, |
/* 70 "F" */ CWORD_CWORD_CWORD_CWORD, |
2799 |
/* 69 -61 */ CWORD_CWORD_CWORD_CWORD, |
/* 71 "G" */ CWORD_CWORD_CWORD_CWORD, |
2800 |
/* 70 -60 */ CWORD_CWORD_CWORD_CWORD, |
/* 72 "H" */ CWORD_CWORD_CWORD_CWORD, |
2801 |
/* 71 -59 */ CWORD_CWORD_CWORD_CWORD, |
/* 73 "I" */ CWORD_CWORD_CWORD_CWORD, |
2802 |
/* 72 -58 */ CWORD_CWORD_CWORD_CWORD, |
/* 74 "J" */ CWORD_CWORD_CWORD_CWORD, |
2803 |
/* 73 -57 */ CWORD_CWORD_CWORD_CWORD, |
/* 75 "K" */ CWORD_CWORD_CWORD_CWORD, |
2804 |
/* 74 -56 */ CWORD_CWORD_CWORD_CWORD, |
/* 76 "L" */ CWORD_CWORD_CWORD_CWORD, |
2805 |
/* 75 -55 */ CWORD_CWORD_CWORD_CWORD, |
/* 77 "M" */ CWORD_CWORD_CWORD_CWORD, |
2806 |
/* 76 -54 */ CWORD_CWORD_CWORD_CWORD, |
/* 78 "N" */ CWORD_CWORD_CWORD_CWORD, |
2807 |
/* 77 -53 */ CWORD_CWORD_CWORD_CWORD, |
/* 79 "O" */ CWORD_CWORD_CWORD_CWORD, |
2808 |
/* 78 -52 */ CWORD_CWORD_CWORD_CWORD, |
/* 80 "P" */ CWORD_CWORD_CWORD_CWORD, |
2809 |
/* 79 -51 */ CWORD_CWORD_CWORD_CWORD, |
/* 81 "Q" */ CWORD_CWORD_CWORD_CWORD, |
2810 |
/* 80 -50 */ CWORD_CWORD_CWORD_CWORD, |
/* 82 "R" */ CWORD_CWORD_CWORD_CWORD, |
2811 |
/* 81 -49 */ CWORD_CWORD_CWORD_CWORD, |
/* 83 "S" */ CWORD_CWORD_CWORD_CWORD, |
2812 |
/* 82 -48 */ CWORD_CWORD_CWORD_CWORD, |
/* 84 "T" */ CWORD_CWORD_CWORD_CWORD, |
2813 |
/* 83 -47 */ CWORD_CWORD_CWORD_CWORD, |
/* 85 "U" */ CWORD_CWORD_CWORD_CWORD, |
2814 |
/* 84 -46 */ CWORD_CWORD_CWORD_CWORD, |
/* 86 "V" */ CWORD_CWORD_CWORD_CWORD, |
2815 |
/* 85 -45 */ CWORD_CWORD_CWORD_CWORD, |
/* 87 "W" */ CWORD_CWORD_CWORD_CWORD, |
2816 |
/* 86 -44 */ CWORD_CWORD_CWORD_CWORD, |
/* 88 "X" */ CWORD_CWORD_CWORD_CWORD, |
2817 |
/* 87 -43 */ CWORD_CWORD_CWORD_CWORD, |
/* 89 "Y" */ CWORD_CWORD_CWORD_CWORD, |
2818 |
/* 88 -42 */ CWORD_CWORD_CWORD_CWORD, |
/* 90 "Z" */ CWORD_CWORD_CWORD_CWORD, |
2819 |
/* 89 -41 */ CWORD_CWORD_CWORD_CWORD, |
/* 91 "[" */ CWORD_CCTL_CCTL_CWORD, |
2820 |
/* 90 -40 */ CWORD_CWORD_CWORD_CWORD, |
/* 92 "\" */ CBACK_CBACK_CCTL_CBACK, |
2821 |
/* 91 -39 */ CWORD_CWORD_CWORD_CWORD, |
/* 93 "]" */ CWORD_CCTL_CCTL_CWORD, |
2822 |
/* 92 -38 */ CWORD_CWORD_CWORD_CWORD, |
/* 94 "^" */ CWORD_CWORD_CWORD_CWORD, |
2823 |
/* 93 -37 */ CWORD_CWORD_CWORD_CWORD, |
/* 95 "_" */ CWORD_CWORD_CWORD_CWORD, |
2824 |
/* 94 -36 */ CWORD_CWORD_CWORD_CWORD, |
/* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE, |
2825 |
/* 95 -35 */ CWORD_CWORD_CWORD_CWORD, |
/* 97 "a" */ CWORD_CWORD_CWORD_CWORD, |
2826 |
/* 96 -34 */ CWORD_CWORD_CWORD_CWORD, |
/* 98 "b" */ CWORD_CWORD_CWORD_CWORD, |
2827 |
/* 97 -33 */ CWORD_CWORD_CWORD_CWORD, |
/* 99 "c" */ CWORD_CWORD_CWORD_CWORD, |
2828 |
/* 98 -32 */ CWORD_CWORD_CWORD_CWORD, |
/* 100 "d" */ CWORD_CWORD_CWORD_CWORD, |
2829 |
/* 99 -31 */ CWORD_CWORD_CWORD_CWORD, |
/* 101 "e" */ CWORD_CWORD_CWORD_CWORD, |
2830 |
/* 100 -30 */ CWORD_CWORD_CWORD_CWORD, |
/* 102 "f" */ CWORD_CWORD_CWORD_CWORD, |
2831 |
/* 101 -29 */ CWORD_CWORD_CWORD_CWORD, |
/* 103 "g" */ CWORD_CWORD_CWORD_CWORD, |
2832 |
/* 102 -28 */ CWORD_CWORD_CWORD_CWORD, |
/* 104 "h" */ CWORD_CWORD_CWORD_CWORD, |
2833 |
/* 103 -27 */ CWORD_CWORD_CWORD_CWORD, |
/* 105 "i" */ CWORD_CWORD_CWORD_CWORD, |
2834 |
/* 104 -26 */ CWORD_CWORD_CWORD_CWORD, |
/* 106 "j" */ CWORD_CWORD_CWORD_CWORD, |
2835 |
/* 105 -25 */ CWORD_CWORD_CWORD_CWORD, |
/* 107 "k" */ CWORD_CWORD_CWORD_CWORD, |
2836 |
/* 106 -24 */ CWORD_CWORD_CWORD_CWORD, |
/* 108 "l" */ CWORD_CWORD_CWORD_CWORD, |
2837 |
/* 107 -23 */ CWORD_CWORD_CWORD_CWORD, |
/* 109 "m" */ CWORD_CWORD_CWORD_CWORD, |
2838 |
/* 108 -22 */ CWORD_CWORD_CWORD_CWORD, |
/* 110 "n" */ CWORD_CWORD_CWORD_CWORD, |
2839 |
/* 109 -21 */ CWORD_CWORD_CWORD_CWORD, |
/* 111 "o" */ CWORD_CWORD_CWORD_CWORD, |
2840 |
/* 110 -20 */ CWORD_CWORD_CWORD_CWORD, |
/* 112 "p" */ CWORD_CWORD_CWORD_CWORD, |
2841 |
/* 111 -19 */ CWORD_CWORD_CWORD_CWORD, |
/* 113 "q" */ CWORD_CWORD_CWORD_CWORD, |
2842 |
/* 112 -18 */ CWORD_CWORD_CWORD_CWORD, |
/* 114 "r" */ CWORD_CWORD_CWORD_CWORD, |
2843 |
/* 113 -17 */ CWORD_CWORD_CWORD_CWORD, |
/* 115 "s" */ CWORD_CWORD_CWORD_CWORD, |
2844 |
/* 114 -16 */ CWORD_CWORD_CWORD_CWORD, |
/* 116 "t" */ CWORD_CWORD_CWORD_CWORD, |
2845 |
/* 115 -15 */ CWORD_CWORD_CWORD_CWORD, |
/* 117 "u" */ CWORD_CWORD_CWORD_CWORD, |
2846 |
/* 116 -14 */ CWORD_CWORD_CWORD_CWORD, |
/* 118 "v" */ CWORD_CWORD_CWORD_CWORD, |
2847 |
/* 117 -13 */ CWORD_CWORD_CWORD_CWORD, |
/* 119 "w" */ CWORD_CWORD_CWORD_CWORD, |
2848 |
/* 118 -12 */ CWORD_CWORD_CWORD_CWORD, |
/* 120 "x" */ CWORD_CWORD_CWORD_CWORD, |
2849 |
/* 119 -11 */ CWORD_CWORD_CWORD_CWORD, |
/* 121 "y" */ CWORD_CWORD_CWORD_CWORD, |
2850 |
/* 120 -10 */ CWORD_CWORD_CWORD_CWORD, |
/* 122 "z" */ CWORD_CWORD_CWORD_CWORD, |
2851 |
/* 121 -9 */ CWORD_CWORD_CWORD_CWORD, |
/* 123 "{" */ CWORD_CWORD_CWORD_CWORD, |
2852 |
/* 122 -8 */ CWORD_CWORD_CWORD_CWORD, |
/* 124 "|" */ CSPCL_CWORD_CWORD_CWORD, |
2853 |
/* 123 -7 */ CWORD_CWORD_CWORD_CWORD, |
/* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR, |
2854 |
/* 124 -6 */ CWORD_CWORD_CWORD_CWORD, |
/* 126 "~" */ CWORD_CCTL_CCTL_CWORD, |
2855 |
/* 125 -5 */ CWORD_CWORD_CWORD_CWORD, |
/* 127 del */ CWORD_CWORD_CWORD_CWORD, |
2856 |
/* 126 -4 */ CWORD_CWORD_CWORD_CWORD, |
/* 128 0x80 */ CWORD_CWORD_CWORD_CWORD, |
2857 |
/* 127 -3 */ CWORD_CWORD_CWORD_CWORD, |
/* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL, |
2858 |
/* 128 -2 */ CWORD_CWORD_CWORD_CWORD, |
/* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL, |
2859 |
/* 129 -1 */ CWORD_CWORD_CWORD_CWORD, |
/* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL, |
2860 |
/* 130 0 */ CWORD_CWORD_CWORD_CWORD, |
/* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL, |
2861 |
/* 131 1 */ CWORD_CWORD_CWORD_CWORD, |
/* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL, |
2862 |
/* 132 2 */ CWORD_CWORD_CWORD_CWORD, |
/* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL, |
2863 |
/* 133 3 */ CWORD_CWORD_CWORD_CWORD, |
/* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL, |
2864 |
/* 134 4 */ CWORD_CWORD_CWORD_CWORD, |
/* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL, |
2865 |
/* 135 5 */ CWORD_CWORD_CWORD_CWORD, |
/* 137 */ CWORD_CWORD_CWORD_CWORD, |
2866 |
/* 136 6 */ CWORD_CWORD_CWORD_CWORD, |
/* 138 */ CWORD_CWORD_CWORD_CWORD, |
2867 |
/* 137 7 */ CWORD_CWORD_CWORD_CWORD, |
/* 139 */ CWORD_CWORD_CWORD_CWORD, |
2868 |
/* 138 8 */ CWORD_CWORD_CWORD_CWORD, |
/* 140 */ CWORD_CWORD_CWORD_CWORD, |
2869 |
/* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD, |
/* 141 */ CWORD_CWORD_CWORD_CWORD, |
2870 |
/* 140 10 "\n" */ CNL_CNL_CNL_CNL, |
/* 142 */ CWORD_CWORD_CWORD_CWORD, |
2871 |
/* 141 11 */ CWORD_CWORD_CWORD_CWORD, |
/* 143 */ CWORD_CWORD_CWORD_CWORD, |
2872 |
/* 142 12 */ CWORD_CWORD_CWORD_CWORD, |
/* 144 */ CWORD_CWORD_CWORD_CWORD, |
2873 |
/* 143 13 */ CWORD_CWORD_CWORD_CWORD, |
/* 145 */ CWORD_CWORD_CWORD_CWORD, |
2874 |
/* 144 14 */ CWORD_CWORD_CWORD_CWORD, |
/* 146 */ CWORD_CWORD_CWORD_CWORD, |
2875 |
/* 145 15 */ CWORD_CWORD_CWORD_CWORD, |
/* 147 */ CWORD_CWORD_CWORD_CWORD, |
2876 |
/* 146 16 */ CWORD_CWORD_CWORD_CWORD, |
/* 148 */ CWORD_CWORD_CWORD_CWORD, |
2877 |
/* 147 17 */ CWORD_CWORD_CWORD_CWORD, |
/* 149 */ CWORD_CWORD_CWORD_CWORD, |
2878 |
/* 148 18 */ CWORD_CWORD_CWORD_CWORD, |
/* 150 */ CWORD_CWORD_CWORD_CWORD, |
2879 |
/* 149 19 */ CWORD_CWORD_CWORD_CWORD, |
/* 151 */ CWORD_CWORD_CWORD_CWORD, |
2880 |
/* 150 20 */ CWORD_CWORD_CWORD_CWORD, |
/* 152 */ CWORD_CWORD_CWORD_CWORD, |
2881 |
/* 151 21 */ CWORD_CWORD_CWORD_CWORD, |
/* 153 */ CWORD_CWORD_CWORD_CWORD, |
2882 |
/* 152 22 */ CWORD_CWORD_CWORD_CWORD, |
/* 154 */ CWORD_CWORD_CWORD_CWORD, |
2883 |
/* 153 23 */ CWORD_CWORD_CWORD_CWORD, |
/* 155 */ CWORD_CWORD_CWORD_CWORD, |
2884 |
/* 154 24 */ CWORD_CWORD_CWORD_CWORD, |
/* 156 */ CWORD_CWORD_CWORD_CWORD, |
2885 |
/* 155 25 */ CWORD_CWORD_CWORD_CWORD, |
/* 157 */ CWORD_CWORD_CWORD_CWORD, |
2886 |
/* 156 26 */ CWORD_CWORD_CWORD_CWORD, |
/* 158 */ CWORD_CWORD_CWORD_CWORD, |
2887 |
/* 157 27 */ CWORD_CWORD_CWORD_CWORD, |
/* 159 */ CWORD_CWORD_CWORD_CWORD, |
2888 |
/* 158 28 */ CWORD_CWORD_CWORD_CWORD, |
/* 160 */ CWORD_CWORD_CWORD_CWORD, |
2889 |
/* 159 29 */ CWORD_CWORD_CWORD_CWORD, |
/* 161 */ CWORD_CWORD_CWORD_CWORD, |
2890 |
/* 160 30 */ CWORD_CWORD_CWORD_CWORD, |
/* 162 */ CWORD_CWORD_CWORD_CWORD, |
2891 |
/* 161 31 */ CWORD_CWORD_CWORD_CWORD, |
/* 163 */ CWORD_CWORD_CWORD_CWORD, |
2892 |
/* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD, |
/* 164 */ CWORD_CWORD_CWORD_CWORD, |
2893 |
/* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD, |
/* 165 */ CWORD_CWORD_CWORD_CWORD, |
2894 |
/* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD, |
/* 166 */ CWORD_CWORD_CWORD_CWORD, |
2895 |
/* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD, |
/* 167 */ CWORD_CWORD_CWORD_CWORD, |
2896 |
/* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR, |
/* 168 */ CWORD_CWORD_CWORD_CWORD, |
2897 |
/* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD, |
/* 169 */ CWORD_CWORD_CWORD_CWORD, |
2898 |
/* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD, |
/* 170 */ CWORD_CWORD_CWORD_CWORD, |
2899 |
/* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD, |
/* 171 */ CWORD_CWORD_CWORD_CWORD, |
2900 |
/* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP, |
/* 172 */ CWORD_CWORD_CWORD_CWORD, |
2901 |
/* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP, |
/* 173 */ CWORD_CWORD_CWORD_CWORD, |
2902 |
/* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD, |
/* 174 */ CWORD_CWORD_CWORD_CWORD, |
2903 |
/* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD, |
/* 175 */ CWORD_CWORD_CWORD_CWORD, |
2904 |
/* 174 44 "," */ CWORD_CWORD_CWORD_CWORD, |
/* 176 */ CWORD_CWORD_CWORD_CWORD, |
2905 |
/* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD, |
/* 177 */ CWORD_CWORD_CWORD_CWORD, |
2906 |
/* 176 46 "." */ CWORD_CWORD_CWORD_CWORD, |
/* 178 */ CWORD_CWORD_CWORD_CWORD, |
2907 |
/* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD, |
/* 179 */ CWORD_CWORD_CWORD_CWORD, |
2908 |
/* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD, |
/* 180 */ CWORD_CWORD_CWORD_CWORD, |
2909 |
/* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD, |
/* 181 */ CWORD_CWORD_CWORD_CWORD, |
2910 |
/* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD, |
/* 182 */ CWORD_CWORD_CWORD_CWORD, |
2911 |
/* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD, |
/* 183 */ CWORD_CWORD_CWORD_CWORD, |
2912 |
/* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD, |
/* 184 */ CWORD_CWORD_CWORD_CWORD, |
2913 |
/* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD, |
/* 185 */ CWORD_CWORD_CWORD_CWORD, |
2914 |
/* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD, |
/* 186 */ CWORD_CWORD_CWORD_CWORD, |
2915 |
/* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD, |
/* 187 */ CWORD_CWORD_CWORD_CWORD, |
2916 |
/* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD, |
/* 188 */ CWORD_CWORD_CWORD_CWORD, |
2917 |
/* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD, |
/* 189 */ CWORD_CWORD_CWORD_CWORD, |
2918 |
/* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD, |
/* 190 */ CWORD_CWORD_CWORD_CWORD, |
2919 |
/* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD, |
/* 191 */ CWORD_CWORD_CWORD_CWORD, |
2920 |
/* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD, |
/* 192 */ CWORD_CWORD_CWORD_CWORD, |
2921 |
/* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD, |
/* 193 */ CWORD_CWORD_CWORD_CWORD, |
2922 |
/* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD, |
/* 194 */ CWORD_CWORD_CWORD_CWORD, |
2923 |
/* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD, |
/* 195 */ CWORD_CWORD_CWORD_CWORD, |
2924 |
/* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD, |
/* 196 */ CWORD_CWORD_CWORD_CWORD, |
2925 |
/* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD, |
/* 197 */ CWORD_CWORD_CWORD_CWORD, |
2926 |
/* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD, |
/* 198 */ CWORD_CWORD_CWORD_CWORD, |
2927 |
/* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD, |
/* 199 */ CWORD_CWORD_CWORD_CWORD, |
2928 |
/* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD, |
/* 200 */ CWORD_CWORD_CWORD_CWORD, |
2929 |
/* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD, |
/* 201 */ CWORD_CWORD_CWORD_CWORD, |
2930 |
/* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD, |
/* 202 */ CWORD_CWORD_CWORD_CWORD, |
2931 |
/* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD, |
/* 203 */ CWORD_CWORD_CWORD_CWORD, |
2932 |
/* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD, |
/* 204 */ CWORD_CWORD_CWORD_CWORD, |
2933 |
/* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD, |
/* 205 */ CWORD_CWORD_CWORD_CWORD, |
2934 |
/* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD, |
/* 206 */ CWORD_CWORD_CWORD_CWORD, |
2935 |
/* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD, |
/* 207 */ CWORD_CWORD_CWORD_CWORD, |
2936 |
/* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD, |
/* 208 */ CWORD_CWORD_CWORD_CWORD, |
2937 |
/* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD, |
/* 209 */ CWORD_CWORD_CWORD_CWORD, |
2938 |
/* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD, |
/* 210 */ CWORD_CWORD_CWORD_CWORD, |
2939 |
/* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD, |
/* 211 */ CWORD_CWORD_CWORD_CWORD, |
2940 |
/* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD, |
/* 212 */ CWORD_CWORD_CWORD_CWORD, |
2941 |
/* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD, |
/* 213 */ CWORD_CWORD_CWORD_CWORD, |
2942 |
/* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD, |
/* 214 */ CWORD_CWORD_CWORD_CWORD, |
2943 |
/* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD, |
/* 215 */ CWORD_CWORD_CWORD_CWORD, |
2944 |
/* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD, |
/* 216 */ CWORD_CWORD_CWORD_CWORD, |
2945 |
/* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD, |
/* 217 */ CWORD_CWORD_CWORD_CWORD, |
2946 |
/* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD, |
/* 218 */ CWORD_CWORD_CWORD_CWORD, |
2947 |
/* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD, |
/* 219 */ CWORD_CWORD_CWORD_CWORD, |
2948 |
/* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD, |
/* 220 */ CWORD_CWORD_CWORD_CWORD, |
2949 |
/* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD, |
/* 221 */ CWORD_CWORD_CWORD_CWORD, |
2950 |
/* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD, |
/* 222 */ CWORD_CWORD_CWORD_CWORD, |
2951 |
/* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD, |
/* 223 */ CWORD_CWORD_CWORD_CWORD, |
2952 |
/* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK, |
/* 224 */ CWORD_CWORD_CWORD_CWORD, |
2953 |
/* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD, |
/* 225 */ CWORD_CWORD_CWORD_CWORD, |
2954 |
/* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD, |
/* 226 */ CWORD_CWORD_CWORD_CWORD, |
2955 |
/* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD, |
/* 227 */ CWORD_CWORD_CWORD_CWORD, |
2956 |
/* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE, |
/* 228 */ CWORD_CWORD_CWORD_CWORD, |
2957 |
/* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD, |
/* 229 */ CWORD_CWORD_CWORD_CWORD, |
2958 |
/* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD, |
/* 230 */ CWORD_CWORD_CWORD_CWORD, |
2959 |
/* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD, |
/* 231 */ CWORD_CWORD_CWORD_CWORD, |
2960 |
/* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD, |
/* 232 */ CWORD_CWORD_CWORD_CWORD, |
2961 |
/* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD, |
/* 233 */ CWORD_CWORD_CWORD_CWORD, |
2962 |
/* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD, |
/* 234 */ CWORD_CWORD_CWORD_CWORD, |
2963 |
/* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD, |
/* 235 */ CWORD_CWORD_CWORD_CWORD, |
2964 |
/* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD, |
/* 236 */ CWORD_CWORD_CWORD_CWORD, |
2965 |
/* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD, |
/* 237 */ CWORD_CWORD_CWORD_CWORD, |
2966 |
/* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD, |
/* 238 */ CWORD_CWORD_CWORD_CWORD, |
2967 |
/* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD, |
/* 239 */ CWORD_CWORD_CWORD_CWORD, |
2968 |
/* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD, |
/* 230 */ CWORD_CWORD_CWORD_CWORD, |
2969 |
/* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD, |
/* 241 */ CWORD_CWORD_CWORD_CWORD, |
2970 |
/* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD, |
/* 242 */ CWORD_CWORD_CWORD_CWORD, |
2971 |
/* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD, |
/* 243 */ CWORD_CWORD_CWORD_CWORD, |
2972 |
/* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD, |
/* 244 */ CWORD_CWORD_CWORD_CWORD, |
2973 |
/* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD, |
/* 245 */ CWORD_CWORD_CWORD_CWORD, |
2974 |
/* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD, |
/* 246 */ CWORD_CWORD_CWORD_CWORD, |
2975 |
/* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD, |
/* 247 */ CWORD_CWORD_CWORD_CWORD, |
2976 |
/* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD, |
/* 248 */ CWORD_CWORD_CWORD_CWORD, |
2977 |
/* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD, |
/* 249 */ CWORD_CWORD_CWORD_CWORD, |
2978 |
/* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD, |
/* 250 */ CWORD_CWORD_CWORD_CWORD, |
2979 |
/* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD, |
/* 251 */ CWORD_CWORD_CWORD_CWORD, |
2980 |
/* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD, |
/* 252 */ CWORD_CWORD_CWORD_CWORD, |
2981 |
/* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD, |
/* 253 */ CWORD_CWORD_CWORD_CWORD, |
2982 |
/* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD, |
/* 254 */ CWORD_CWORD_CWORD_CWORD, |
2983 |
/* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD, |
/* 255 */ CWORD_CWORD_CWORD_CWORD, |
2984 |
/* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD, |
/* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE, |
2985 |
/* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR, |
# if ENABLE_ASH_ALIAS |
2986 |
/* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD, |
/* PEOA */ CSPCL_CIGN_CIGN_CIGN, |
2987 |
/* 257 127 */ CWORD_CWORD_CWORD_CWORD, |
# endif |
2988 |
}; |
}; |
2989 |
|
|
2990 |
#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]) |
# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf) |
2991 |
|
|
2992 |
#endif /* USE_SIT_FUNCTION */ |
#endif /* !USE_SIT_FUNCTION */ |
2993 |
|
|
2994 |
|
|
2995 |
/* ============ Alias handling */ |
/* ============ Alias handling */ |
3137 |
/* |
/* |
3138 |
* TODO - sort output |
* TODO - sort output |
3139 |
*/ |
*/ |
3140 |
static int |
static int FAST_FUNC |
3141 |
aliascmd(int argc UNUSED_PARAM, char **argv) |
aliascmd(int argc UNUSED_PARAM, char **argv) |
3142 |
{ |
{ |
3143 |
char *n, *v; |
char *n, *v; |
3172 |
return ret; |
return ret; |
3173 |
} |
} |
3174 |
|
|
3175 |
static int |
static int FAST_FUNC |
3176 |
unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
3177 |
{ |
{ |
3178 |
int i; |
int i; |
3199 |
/* ============ jobs.c */ |
/* ============ jobs.c */ |
3200 |
|
|
3201 |
/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ |
/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ |
3202 |
#define FORK_FG 0 |
#define FORK_FG 0 |
3203 |
#define FORK_BG 1 |
#define FORK_BG 1 |
3204 |
#define FORK_NOJOB 2 |
#define FORK_NOJOB 2 |
3205 |
|
|
3206 |
/* mode flags for showjob(s) */ |
/* mode flags for showjob(s) */ |
3207 |
#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */ |
#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */ |
3208 |
#define SHOW_PID 0x04 /* include process pid */ |
#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */ |
3209 |
#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */ |
#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */ |
3210 |
|
|
3211 |
/* |
/* |
3212 |
* A job structure contains information about a job. A job is either a |
* A job structure contains information about a job. A job is either a |
3214 |
* latter case, pidlist will be non-NULL, and will point to a -1 terminated |
* latter case, pidlist will be non-NULL, and will point to a -1 terminated |
3215 |
* array of pids. |
* array of pids. |
3216 |
*/ |
*/ |
|
|
|
3217 |
struct procstat { |
struct procstat { |
3218 |
pid_t pid; /* process id */ |
pid_t ps_pid; /* process id */ |
3219 |
int status; /* last process status from wait() */ |
int ps_status; /* last process status from wait() */ |
3220 |
char *cmd; /* text of command being run */ |
char *ps_cmd; /* text of command being run */ |
3221 |
}; |
}; |
3222 |
|
|
3223 |
struct job { |
struct job { |
3243 |
}; |
}; |
3244 |
|
|
3245 |
static struct job *makejob(/*union node *,*/ int); |
static struct job *makejob(/*union node *,*/ int); |
|
#if !JOBS |
|
|
#define forkshell(job, node, mode) forkshell(job, mode) |
|
|
#endif |
|
3246 |
static int forkshell(struct job *, union node *, int); |
static int forkshell(struct job *, union node *, int); |
3247 |
static int waitforjob(struct job *); |
static int waitforjob(struct job *); |
3248 |
|
|
3255 |
#endif |
#endif |
3256 |
|
|
3257 |
/* |
/* |
3258 |
|
* Ignore a signal. |
3259 |
|
*/ |
3260 |
|
static void |
3261 |
|
ignoresig(int signo) |
3262 |
|
{ |
3263 |
|
/* Avoid unnecessary system calls. Is it already SIG_IGNed? */ |
3264 |
|
if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) { |
3265 |
|
/* No, need to do it */ |
3266 |
|
signal(signo, SIG_IGN); |
3267 |
|
} |
3268 |
|
sigmode[signo - 1] = S_HARD_IGN; |
3269 |
|
} |
3270 |
|
|
3271 |
|
/* |
3272 |
|
* Signal handler. Only one usage site - in setsignal() |
3273 |
|
*/ |
3274 |
|
static void |
3275 |
|
onsig(int signo) |
3276 |
|
{ |
3277 |
|
gotsig[signo - 1] = 1; |
3278 |
|
|
3279 |
|
if (signo == SIGINT && !trap[SIGINT]) { |
3280 |
|
if (!suppress_int) { |
3281 |
|
pending_sig = 0; |
3282 |
|
raise_interrupt(); /* does not return */ |
3283 |
|
} |
3284 |
|
pending_int = 1; |
3285 |
|
} else { |
3286 |
|
pending_sig = signo; |
3287 |
|
} |
3288 |
|
} |
3289 |
|
|
3290 |
|
/* |
3291 |
* Set the signal handler for the specified signal. The routine figures |
* Set the signal handler for the specified signal. The routine figures |
3292 |
* out what it should be set to. |
* out what it should be set to. |
3293 |
*/ |
*/ |
3294 |
static void |
static void |
3295 |
setsignal(int signo) |
setsignal(int signo) |
3296 |
{ |
{ |
3297 |
int action; |
char *t; |
3298 |
char *t, tsig; |
char cur_act, new_act; |
3299 |
struct sigaction act; |
struct sigaction act; |
3300 |
|
|
3301 |
t = trap[signo]; |
t = trap[signo]; |
3302 |
action = S_IGN; |
new_act = S_DFL; |
3303 |
if (t == NULL) |
if (t != NULL) { /* trap for this sig is set */ |
3304 |
action = S_DFL; |
new_act = S_CATCH; |
3305 |
else if (*t != '\0') |
if (t[0] == '\0') /* trap is "": ignore this sig */ |
3306 |
action = S_CATCH; |
new_act = S_IGN; |
3307 |
if (rootshell && action == S_DFL) { |
} |
3308 |
|
|
3309 |
|
if (rootshell && new_act == S_DFL) { |
3310 |
switch (signo) { |
switch (signo) { |
3311 |
case SIGINT: |
case SIGINT: |
3312 |
if (iflag || minusc || sflag == 0) |
if (iflag || minusc || sflag == 0) |
3313 |
action = S_CATCH; |
new_act = S_CATCH; |
3314 |
break; |
break; |
3315 |
case SIGQUIT: |
case SIGQUIT: |
3316 |
#if DEBUG |
#if DEBUG |
3317 |
if (debug) |
if (debug) |
3318 |
break; |
break; |
3319 |
#endif |
#endif |
3320 |
/* FALLTHROUGH */ |
/* man bash: |
3321 |
|
* "In all cases, bash ignores SIGQUIT. Non-builtin |
3322 |
|
* commands run by bash have signal handlers |
3323 |
|
* set to the values inherited by the shell |
3324 |
|
* from its parent". */ |
3325 |
|
new_act = S_IGN; |
3326 |
|
break; |
3327 |
case SIGTERM: |
case SIGTERM: |
3328 |
if (iflag) |
if (iflag) |
3329 |
action = S_IGN; |
new_act = S_IGN; |
3330 |
break; |
break; |
3331 |
#if JOBS |
#if JOBS |
3332 |
case SIGTSTP: |
case SIGTSTP: |
3333 |
case SIGTTOU: |
case SIGTTOU: |
3334 |
if (mflag) |
if (mflag) |
3335 |
action = S_IGN; |
new_act = S_IGN; |
3336 |
break; |
break; |
3337 |
#endif |
#endif |
3338 |
} |
} |
3339 |
} |
} |
3340 |
|
//TODO: if !rootshell, we reset SIGQUIT to DFL, |
3341 |
|
//whereas we have to restore it to what shell got on entry |
3342 |
|
//from the parent. See comment above |
3343 |
|
|
3344 |
t = &sigmode[signo - 1]; |
t = &sigmode[signo - 1]; |
3345 |
tsig = *t; |
cur_act = *t; |
3346 |
if (tsig == 0) { |
if (cur_act == 0) { |
3347 |
/* |
/* current setting is not yet known */ |
3348 |
* current setting unknown |
if (sigaction(signo, NULL, &act)) { |
3349 |
*/ |
/* pretend it worked; maybe we should give a warning, |
3350 |
if (sigaction(signo, NULL, &act) == -1) { |
* but other shells don't. We don't alter sigmode, |
3351 |
/* |
* so we retry every time. |
3352 |
* Pretend it worked; maybe we should give a warning |
* btw, in Linux it never fails. --vda */ |
|
* here, but other shells don't. We don't alter |
|
|
* sigmode, so that we retry every time. |
|
|
*/ |
|
3353 |
return; |
return; |
3354 |
} |
} |
|
tsig = S_RESET; /* force to be set */ |
|
3355 |
if (act.sa_handler == SIG_IGN) { |
if (act.sa_handler == SIG_IGN) { |
3356 |
tsig = S_HARD_IGN; |
cur_act = S_HARD_IGN; |
3357 |
if (mflag |
if (mflag |
3358 |
&& (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU) |
&& (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU) |
3359 |
) { |
) { |
3360 |
tsig = S_IGN; /* don't hard ignore these */ |
cur_act = S_IGN; /* don't hard ignore these */ |
3361 |
} |
} |
3362 |
} |
} |
3363 |
} |
} |
3364 |
if (tsig == S_HARD_IGN || tsig == action) |
if (cur_act == S_HARD_IGN || cur_act == new_act) |
3365 |
return; |
return; |
3366 |
|
|
3367 |
act.sa_handler = SIG_DFL; |
act.sa_handler = SIG_DFL; |
3368 |
switch (action) { |
switch (new_act) { |
3369 |
case S_CATCH: |
case S_CATCH: |
3370 |
act.sa_handler = onsig; |
act.sa_handler = onsig; |
3371 |
|
act.sa_flags = 0; /* matters only if !DFL and !IGN */ |
3372 |
|
sigfillset(&act.sa_mask); /* ditto */ |
3373 |
break; |
break; |
3374 |
case S_IGN: |
case S_IGN: |
3375 |
act.sa_handler = SIG_IGN; |
act.sa_handler = SIG_IGN; |
3376 |
break; |
break; |
3377 |
} |
} |
|
*t = action; |
|
|
act.sa_flags = 0; |
|
|
sigfillset(&act.sa_mask); |
|
3378 |
sigaction_set(signo, &act); |
sigaction_set(signo, &act); |
3379 |
|
|
3380 |
|
*t = new_act; |
3381 |
} |
} |
3382 |
|
|
3383 |
/* mode flags for set_curjob */ |
/* mode flags for set_curjob */ |
3470 |
{ |
{ |
3471 |
struct job *jp; |
struct job *jp; |
3472 |
struct job *found; |
struct job *found; |
3473 |
const char *err_msg = "No such job: %s"; |
const char *err_msg = "%s: no such job"; |
3474 |
unsigned num; |
unsigned num; |
3475 |
int c; |
int c; |
3476 |
const char *p; |
const char *p; |
3506 |
} |
} |
3507 |
|
|
3508 |
if (is_number(p)) { |
if (is_number(p)) { |
|
// TODO: number() instead? It does error checking... |
|
3509 |
num = atoi(p); |
num = atoi(p); |
3510 |
if (num < njobs) { |
if (num < njobs) { |
3511 |
jp = jobtab + num - 1; |
jp = jobtab + num - 1; |
3521 |
p++; |
p++; |
3522 |
} |
} |
3523 |
|
|
3524 |
found = 0; |
found = NULL; |
3525 |
while (1) { |
while (jp) { |
3526 |
if (!jp) |
if (match(jp->ps[0].ps_cmd, p)) { |
|
goto err; |
|
|
if (match(jp->ps[0].cmd, p)) { |
|
3527 |
if (found) |
if (found) |
3528 |
goto err; |
goto err; |
3529 |
found = jp; |
found = jp; |
3531 |
} |
} |
3532 |
jp = jp->prev_job; |
jp = jp->prev_job; |
3533 |
} |
} |
3534 |
|
if (!found) |
3535 |
|
goto err; |
3536 |
|
jp = found; |
3537 |
|
|
3538 |
gotit: |
gotit: |
3539 |
#if JOBS |
#if JOBS |
3557 |
|
|
3558 |
INT_OFF; |
INT_OFF; |
3559 |
for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) { |
for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) { |
3560 |
if (ps->cmd != nullstr) |
if (ps->ps_cmd != nullstr) |
3561 |
free(ps->cmd); |
free(ps->ps_cmd); |
3562 |
} |
} |
3563 |
if (jp->ps != &jp->ps0) |
if (jp->ps != &jp->ps0) |
3564 |
free(jp->ps); |
free(jp->ps); |
3652 |
doing_jobctl = on; |
doing_jobctl = on; |
3653 |
} |
} |
3654 |
|
|
3655 |
static int |
static int FAST_FUNC |
3656 |
killcmd(int argc, char **argv) |
killcmd(int argc, char **argv) |
3657 |
{ |
{ |
3658 |
int i = 1; |
int i = 1; |
3660 |
do { |
do { |
3661 |
if (argv[i][0] == '%') { |
if (argv[i][0] == '%') { |
3662 |
struct job *jp = getjob(argv[i], 0); |
struct job *jp = getjob(argv[i], 0); |
3663 |
unsigned pid = jp->ps[0].pid; |
unsigned pid = jp->ps[0].ps_pid; |
3664 |
/* Enough space for ' -NNN<nul>' */ |
/* Enough space for ' -NNN<nul>' */ |
3665 |
argv[i] = alloca(sizeof(int)*3 + 3); |
argv[i] = alloca(sizeof(int)*3 + 3); |
3666 |
/* kill_main has matching code to expect |
/* kill_main has matching code to expect |
3674 |
} |
} |
3675 |
|
|
3676 |
static void |
static void |
3677 |
showpipe(struct job *jp, FILE *out) |
showpipe(struct job *jp /*, FILE *out*/) |
3678 |
{ |
{ |
3679 |
struct procstat *sp; |
struct procstat *ps; |
3680 |
struct procstat *spend; |
struct procstat *psend; |
3681 |
|
|
3682 |
spend = jp->ps + jp->nprocs; |
psend = jp->ps + jp->nprocs; |
3683 |
for (sp = jp->ps + 1; sp < spend; sp++) |
for (ps = jp->ps + 1; ps < psend; ps++) |
3684 |
fprintf(out, " | %s", sp->cmd); |
printf(" | %s", ps->ps_cmd); |
3685 |
outcslow('\n', out); |
outcslow('\n', stdout); |
3686 |
flush_stdout_stderr(); |
flush_stdout_stderr(); |
3687 |
} |
} |
3688 |
|
|
3699 |
if (jp->state == JOBDONE) |
if (jp->state == JOBDONE) |
3700 |
goto out; |
goto out; |
3701 |
jp->state = JOBRUNNING; |
jp->state = JOBRUNNING; |
3702 |
pgid = jp->ps->pid; |
pgid = jp->ps[0].ps_pid; |
3703 |
if (mode == FORK_FG) |
if (mode == FORK_FG) |
3704 |
xtcsetpgrp(ttyfd, pgid); |
xtcsetpgrp(ttyfd, pgid); |
3705 |
killpg(pgid, SIGCONT); |
killpg(pgid, SIGCONT); |
3706 |
ps = jp->ps; |
ps = jp->ps; |
3707 |
i = jp->nprocs; |
i = jp->nprocs; |
3708 |
do { |
do { |
3709 |
if (WIFSTOPPED(ps->status)) { |
if (WIFSTOPPED(ps->ps_status)) { |
3710 |
ps->status = -1; |
ps->ps_status = -1; |
3711 |
} |
} |
3712 |
ps++; |
ps++; |
3713 |
} while (--i); |
} while (--i); |
3717 |
return status; |
return status; |
3718 |
} |
} |
3719 |
|
|
3720 |
static int |
static int FAST_FUNC |
3721 |
fg_bgcmd(int argc UNUSED_PARAM, char **argv) |
fg_bgcmd(int argc UNUSED_PARAM, char **argv) |
3722 |
{ |
{ |
3723 |
struct job *jp; |
struct job *jp; |
|
FILE *out; |
|
3724 |
int mode; |
int mode; |
3725 |
int retval; |
int retval; |
3726 |
|
|
3727 |
mode = (**argv == 'f') ? FORK_FG : FORK_BG; |
mode = (**argv == 'f') ? FORK_FG : FORK_BG; |
3728 |
nextopt(nullstr); |
nextopt(nullstr); |
3729 |
argv = argptr; |
argv = argptr; |
|
out = stdout; |
|
3730 |
do { |
do { |
3731 |
jp = getjob(*argv, 1); |
jp = getjob(*argv, 1); |
3732 |
if (mode == FORK_BG) { |
if (mode == FORK_BG) { |
3733 |
set_curjob(jp, CUR_RUNNING); |
set_curjob(jp, CUR_RUNNING); |
3734 |
fprintf(out, "[%d] ", jobno(jp)); |
printf("[%d] ", jobno(jp)); |
3735 |
} |
} |
3736 |
outstr(jp->ps->cmd, out); |
out1str(jp->ps[0].ps_cmd); |
3737 |
showpipe(jp, out); |
showpipe(jp /*, stdout*/); |
3738 |
retval = restartjob(jp, mode); |
retval = restartjob(jp, mode); |
3739 |
} while (*argv && *++argv); |
} while (*argv && *++argv); |
3740 |
return retval; |
return retval; |
3793 |
/* Do a wait system call. If job control is compiled in, we accept |
/* Do a wait system call. If job control is compiled in, we accept |
3794 |
* stopped processes. wait_flags may have WNOHANG, preventing blocking. |
* stopped processes. wait_flags may have WNOHANG, preventing blocking. |
3795 |
* NB: _not_ safe_waitpid, we need to detect EINTR */ |
* NB: _not_ safe_waitpid, we need to detect EINTR */ |
3796 |
pid = waitpid(-1, &status, |
if (doing_jobctl) |
3797 |
(doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags)); |
wait_flags |= WUNTRACED; |
3798 |
TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", pid, status, errno, strerror(errno))); |
pid = waitpid(-1, &status, wait_flags); |
3799 |
|
TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", |
3800 |
if (pid <= 0) { |
pid, status, errno, strerror(errno))); |
3801 |
/* If we were doing blocking wait and (probably) got EINTR, |
if (pid <= 0) |
|
* check for pending sigs received while waiting. |
|
|
* (NB: can be moved into callers if needed) */ |
|
|
if (wait_flags == DOWAIT_BLOCK && pendingsig) |
|
|
raise_exception(EXSIG); |
|
3802 |
return pid; |
return pid; |
3803 |
} |
|
3804 |
INT_OFF; |
INT_OFF; |
3805 |
thisjob = NULL; |
thisjob = NULL; |
3806 |
for (jp = curjob; jp; jp = jp->prev_job) { |
for (jp = curjob; jp; jp = jp->prev_job) { |
3807 |
struct procstat *sp; |
struct procstat *ps; |
3808 |
struct procstat *spend; |
struct procstat *psend; |
3809 |
if (jp->state == JOBDONE) |
if (jp->state == JOBDONE) |
3810 |
continue; |
continue; |
3811 |
state = JOBDONE; |
state = JOBDONE; |
3812 |
spend = jp->ps + jp->nprocs; |
ps = jp->ps; |
3813 |
sp = jp->ps; |
psend = ps + jp->nprocs; |
3814 |
do { |
do { |
3815 |
if (sp->pid == pid) { |
if (ps->ps_pid == pid) { |
3816 |
TRACE(("Job %d: changing status of proc %d " |
TRACE(("Job %d: changing status of proc %d " |
3817 |
"from 0x%x to 0x%x\n", |
"from 0x%x to 0x%x\n", |
3818 |
jobno(jp), pid, sp->status, status)); |
jobno(jp), pid, ps->ps_status, status)); |
3819 |
sp->status = status; |
ps->ps_status = status; |
3820 |
thisjob = jp; |
thisjob = jp; |
3821 |
} |
} |
3822 |
if (sp->status == -1) |
if (ps->ps_status == -1) |
3823 |
state = JOBRUNNING; |
state = JOBRUNNING; |
3824 |
#if JOBS |
#if JOBS |
3825 |
if (state == JOBRUNNING) |
if (state == JOBRUNNING) |
3826 |
continue; |
continue; |
3827 |
if (WIFSTOPPED(sp->status)) { |
if (WIFSTOPPED(ps->ps_status)) { |
3828 |
jp->stopstatus = sp->status; |
jp->stopstatus = ps->ps_status; |
3829 |
state = JOBSTOPPED; |
state = JOBSTOPPED; |
3830 |
} |
} |
3831 |
#endif |
#endif |
3832 |
} while (++sp < spend); |
} while (++ps < psend); |
3833 |
if (thisjob) |
if (thisjob) |
3834 |
goto gotjob; |
goto gotjob; |
3835 |
} |
} |
3872 |
return pid; |
return pid; |
3873 |
} |
} |
3874 |
|
|
3875 |
|
static int |
3876 |
|
blocking_wait_with_raise_on_sig(struct job *job) |
3877 |
|
{ |
3878 |
|
pid_t pid = dowait(DOWAIT_BLOCK, job); |
3879 |
|
if (pid <= 0 && pending_sig) |
3880 |
|
raise_exception(EXSIG); |
3881 |
|
return pid; |
3882 |
|
} |
3883 |
|
|
3884 |
#if JOBS |
#if JOBS |
3885 |
static void |
static void |
3886 |
showjob(FILE *out, struct job *jp, int mode) |
showjob(FILE *out, struct job *jp, int mode) |
3893 |
|
|
3894 |
ps = jp->ps; |
ps = jp->ps; |
3895 |
|
|
3896 |
if (mode & SHOW_PGID) { |
if (mode & SHOW_ONLY_PGID) { /* jobs -p */ |
3897 |
/* just output process (group) id of pipeline */ |
/* just output process (group) id of pipeline */ |
3898 |
fprintf(out, "%d\n", ps->pid); |
fprintf(out, "%d\n", ps->ps_pid); |
3899 |
return; |
return; |
3900 |
} |
} |
3901 |
|
|
3903 |
indent_col = col; |
indent_col = col; |
3904 |
|
|
3905 |
if (jp == curjob) |
if (jp == curjob) |
3906 |
s[col - 2] = '+'; |
s[col - 3] = '+'; |
3907 |
else if (curjob && jp == curjob->prev_job) |
else if (curjob && jp == curjob->prev_job) |
3908 |
s[col - 2] = '-'; |
s[col - 3] = '-'; |
3909 |
|
|
3910 |
if (mode & SHOW_PID) |
if (mode & SHOW_PIDS) |
3911 |
col += fmtstr(s + col, 16, "%d ", ps->pid); |
col += fmtstr(s + col, 16, "%d ", ps->ps_pid); |
3912 |
|
|
3913 |
psend = ps + jp->nprocs; |
psend = ps + jp->nprocs; |
3914 |
|
|
3916 |
strcpy(s + col, "Running"); |
strcpy(s + col, "Running"); |
3917 |
col += sizeof("Running") - 1; |
col += sizeof("Running") - 1; |
3918 |
} else { |
} else { |
3919 |
int status = psend[-1].status; |
int status = psend[-1].ps_status; |
3920 |
if (jp->state == JOBSTOPPED) |
if (jp->state == JOBSTOPPED) |
3921 |
status = jp->stopstatus; |
status = jp->stopstatus; |
3922 |
col += sprint_status(s + col, status, 0); |
col += sprint_status(s + col, status, 0); |
3923 |
} |
} |
3924 |
|
/* By now, "[JOBID]* [maybe PID] STATUS" is printed */ |
3925 |
|
|
3926 |
|
/* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line |
3927 |
|
* or prints several "PID | <cmdN>" lines, |
3928 |
|
* depending on SHOW_PIDS bit. |
3929 |
|
* We do not print status of individual processes |
3930 |
|
* between PID and <cmdN>. bash does it, but not very well: |
3931 |
|
* first line shows overall job status, not process status, |
3932 |
|
* making it impossible to know 1st process status. |
3933 |
|
*/ |
3934 |
goto start; |
goto start; |
|
|
|
3935 |
do { |
do { |
3936 |
/* for each process */ |
/* for each process */ |
3937 |
col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3; |
s[0] = '\0'; |
3938 |
|
col = 33; |
3939 |
|
if (mode & SHOW_PIDS) |
3940 |
|
col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1; |
3941 |
start: |
start: |
3942 |
fprintf(out, "%s%*c%s", |
fprintf(out, "%s%*c%s%s", |
3943 |
s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd |
s, |
3944 |
|
33 - col >= 0 ? 33 - col : 0, ' ', |
3945 |
|
ps == jp->ps ? "" : "| ", |
3946 |
|
ps->ps_cmd |
3947 |
); |
); |
3948 |
if (!(mode & SHOW_PID)) { |
} while (++ps != psend); |
3949 |
showpipe(jp, out); |
outcslow('\n', out); |
|
break; |
|
|
} |
|
|
if (++ps == psend) { |
|
|
outcslow('\n', out); |
|
|
break; |
|
|
} |
|
|
} while (1); |
|
3950 |
|
|
3951 |
jp->changed = 0; |
jp->changed = 0; |
3952 |
|
|
3965 |
{ |
{ |
3966 |
struct job *jp; |
struct job *jp; |
3967 |
|
|
3968 |
TRACE(("showjobs(%x) called\n", mode)); |
TRACE(("showjobs(0x%x) called\n", mode)); |
3969 |
|
|
3970 |
/* If not even one job changed, there is nothing to do */ |
/* Handle all finished jobs */ |
3971 |
while (dowait(DOWAIT_NONBLOCK, NULL) > 0) |
while (dowait(DOWAIT_NONBLOCK, NULL) > 0) |
3972 |
continue; |
continue; |
3973 |
|
|
3978 |
} |
} |
3979 |
} |
} |
3980 |
|
|
3981 |
static int |
static int FAST_FUNC |
3982 |
jobscmd(int argc UNUSED_PARAM, char **argv) |
jobscmd(int argc UNUSED_PARAM, char **argv) |
3983 |
{ |
{ |
3984 |
int mode, m; |
int mode, m; |
3985 |
|
|
3986 |
mode = 0; |
mode = 0; |
3987 |
while ((m = nextopt("lp"))) { |
while ((m = nextopt("lp")) != '\0') { |
3988 |
if (m == 'l') |
if (m == 'l') |
3989 |
mode = SHOW_PID; |
mode |= SHOW_PIDS; |
3990 |
else |
else |
3991 |
mode = SHOW_PGID; |
mode |= SHOW_ONLY_PGID; |
3992 |
} |
} |
3993 |
|
|
3994 |
argv = argptr; |
argv = argptr; |
3995 |
if (*argv) { |
if (*argv) { |
3996 |
do |
do |
3997 |
showjob(stdout, getjob(*argv,0), mode); |
showjob(stdout, getjob(*argv, 0), mode); |
3998 |
while (*++argv); |
while (*++argv); |
3999 |
} else |
} else { |
4000 |
showjobs(stdout, mode); |
showjobs(stdout, mode); |
4001 |
|
} |
4002 |
|
|
4003 |
return 0; |
return 0; |
4004 |
} |
} |
4005 |
#endif /* JOBS */ |
#endif /* JOBS */ |
4006 |
|
|
4007 |
|
/* Called only on finished or stopped jobs (no members are running) */ |
4008 |
static int |
static int |
4009 |
getstatus(struct job *job) |
getstatus(struct job *job) |
4010 |
{ |
{ |
4011 |
int status; |
int status; |
4012 |
int retval; |
int retval; |
4013 |
|
struct procstat *ps; |
4014 |
|
|
4015 |
|
/* Fetch last member's status */ |
4016 |
|
ps = job->ps + job->nprocs - 1; |
4017 |
|
status = ps->ps_status; |
4018 |
|
if (pipefail) { |
4019 |
|
/* "set -o pipefail" mode: use last _nonzero_ status */ |
4020 |
|
while (status == 0 && --ps >= job->ps) |
4021 |
|
status = ps->ps_status; |
4022 |
|
} |
4023 |
|
|
|
status = job->ps[job->nprocs - 1].status; |
|
4024 |
retval = WEXITSTATUS(status); |
retval = WEXITSTATUS(status); |
4025 |
if (!WIFEXITED(status)) { |
if (!WIFEXITED(status)) { |
4026 |
#if JOBS |
#if JOBS |
4037 |
} |
} |
4038 |
retval += 128; |
retval += 128; |
4039 |
} |
} |
4040 |
TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n", |
TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n", |
4041 |
jobno(job), job->nprocs, status, retval)); |
jobno(job), job->nprocs, status, retval)); |
4042 |
return retval; |
return retval; |
4043 |
} |
} |
4044 |
|
|
4045 |
static int |
static int FAST_FUNC |
4046 |
waitcmd(int argc UNUSED_PARAM, char **argv) |
waitcmd(int argc UNUSED_PARAM, char **argv) |
4047 |
{ |
{ |
4048 |
struct job *job; |
struct job *job; |
4049 |
int retval; |
int retval; |
4050 |
struct job *jp; |
struct job *jp; |
4051 |
|
|
4052 |
// exsig++; |
if (pending_sig) |
|
// xbarrier(); |
|
|
if (pendingsig) |
|
4053 |
raise_exception(EXSIG); |
raise_exception(EXSIG); |
4054 |
|
|
4055 |
nextopt(nullstr); |
nextopt(nullstr); |
4068 |
jp->waited = 1; |
jp->waited = 1; |
4069 |
jp = jp->prev_job; |
jp = jp->prev_job; |
4070 |
} |
} |
4071 |
dowait(DOWAIT_BLOCK, NULL); |
/* man bash: |
4072 |
|
* "When bash is waiting for an asynchronous command via |
4073 |
|
* the wait builtin, the reception of a signal for which a trap |
4074 |
|
* has been set will cause the wait builtin to return immediately |
4075 |
|
* with an exit status greater than 128, immediately after which |
4076 |
|
* the trap is executed." |
4077 |
|
* Do we do it that way? */ |
4078 |
|
blocking_wait_with_raise_on_sig(NULL); |
4079 |
} |
} |
4080 |
} |
} |
4081 |
|
|
4087 |
while (1) { |
while (1) { |
4088 |
if (!job) |
if (!job) |
4089 |
goto repeat; |
goto repeat; |
4090 |
if (job->ps[job->nprocs - 1].pid == pid) |
if (job->ps[job->nprocs - 1].ps_pid == pid) |
4091 |
break; |
break; |
4092 |
job = job->prev_job; |
job = job->prev_job; |
4093 |
} |
} |
4095 |
job = getjob(*argv, 0); |
job = getjob(*argv, 0); |
4096 |
/* loop until process terminated or stopped */ |
/* loop until process terminated or stopped */ |
4097 |
while (job->state == JOBRUNNING) |
while (job->state == JOBRUNNING) |
4098 |
dowait(DOWAIT_BLOCK, NULL); |
blocking_wait_with_raise_on_sig(NULL); |
4099 |
job->waited = 1; |
job->waited = 1; |
4100 |
retval = getstatus(job); |
retval = getstatus(job); |
4101 |
repeat: |
repeat: ; |
|
; |
|
4102 |
} while (*++argv); |
} while (*++argv); |
4103 |
|
|
4104 |
ret: |
ret: |
4206 |
static const char vstype[VSTYPE + 1][3] = { |
static const char vstype[VSTYPE + 1][3] = { |
4207 |
"", "}", "-", "+", "?", "=", |
"", "}", "-", "+", "?", "=", |
4208 |
"%", "%%", "#", "##" |
"%", "%%", "#", "##" |
4209 |
USE_ASH_BASH_COMPAT(, ":", "/", "//") |
IF_ASH_BASH_COMPAT(, ":", "/", "//") |
4210 |
}; |
}; |
4211 |
|
|
4212 |
const char *p, *str; |
const char *p, *str; |
4213 |
char c, cc[2] = " "; |
char cc[2]; |
4214 |
char *nextc; |
char *nextc; |
4215 |
int subtype = 0; |
unsigned char c; |
4216 |
|
unsigned char subtype = 0; |
4217 |
int quoted = 0; |
int quoted = 0; |
4218 |
|
|
4219 |
|
cc[1] = '\0'; |
4220 |
nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc); |
nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc); |
4221 |
p = s; |
p = s; |
4222 |
while ((c = *p++) != 0) { |
while ((c = *p++) != '\0') { |
4223 |
str = NULL; |
str = NULL; |
4224 |
switch (c) { |
switch (c) { |
4225 |
case CTLESC: |
case CTLESC: |
4247 |
case CTLBACKQ+CTLQUOTE: |
case CTLBACKQ+CTLQUOTE: |
4248 |
str = "\"$(...)\""; |
str = "\"$(...)\""; |
4249 |
goto dostr; |
goto dostr; |
4250 |
#if ENABLE_ASH_MATH_SUPPORT |
#if ENABLE_SH_MATH_SUPPORT |
4251 |
case CTLARI: |
case CTLARI: |
4252 |
str = "$(("; |
str = "$(("; |
4253 |
goto dostr; |
goto dostr; |
4287 |
if (!str) |
if (!str) |
4288 |
continue; |
continue; |
4289 |
dostr: |
dostr: |
4290 |
while ((c = *str++)) { |
while ((c = *str++) != '\0') { |
4291 |
USTPUTC(c, nextc); |
USTPUTC(c, nextc); |
4292 |
} |
} |
4293 |
} |
} /* while *p++ not NUL */ |
4294 |
|
|
4295 |
if (quoted & 1) { |
if (quoted & 1) { |
4296 |
USTPUTC('"', nextc); |
USTPUTC('"', nextc); |
4297 |
} |
} |
4365 |
cmdputs("if "); |
cmdputs("if "); |
4366 |
cmdtxt(n->nif.test); |
cmdtxt(n->nif.test); |
4367 |
cmdputs("; then "); |
cmdputs("; then "); |
|
n = n->nif.ifpart; |
|
4368 |
if (n->nif.elsepart) { |
if (n->nif.elsepart) { |
4369 |
cmdtxt(n); |
cmdtxt(n->nif.ifpart); |
4370 |
cmdputs("; else "); |
cmdputs("; else "); |
4371 |
n = n->nif.elsepart; |
n = n->nif.elsepart; |
4372 |
|
} else { |
4373 |
|
n = n->nif.ifpart; |
4374 |
} |
} |
4375 |
p = "; fi"; |
p = "; fi"; |
4376 |
goto dotail; |
goto dotail; |
4507 |
for (tp = trap; tp < &trap[NSIG]; tp++) { |
for (tp = trap; tp < &trap[NSIG]; tp++) { |
4508 |
if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */ |
if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */ |
4509 |
INT_OFF; |
INT_OFF; |
4510 |
free(*tp); |
if (trap_ptr == trap) |
4511 |
|
free(*tp); |
4512 |
|
/* else: it "belongs" to trap_ptr vector, don't free */ |
4513 |
*tp = NULL; |
*tp = NULL; |
4514 |
if (tp != &trap[0]) |
if ((tp - trap) != 0) |
4515 |
setsignal(tp - trap); |
setsignal(tp - trap); |
4516 |
INT_ON; |
INT_ON; |
4517 |
} |
} |
4522 |
static void closescript(void); |
static void closescript(void); |
4523 |
|
|
4524 |
/* Called after fork(), in child */ |
/* Called after fork(), in child */ |
4525 |
static void |
static NOINLINE void |
4526 |
forkchild(struct job *jp, /*union node *n,*/ int mode) |
forkchild(struct job *jp, union node *n, int mode) |
4527 |
{ |
{ |
4528 |
int oldlvl; |
int oldlvl; |
4529 |
|
|
4531 |
oldlvl = shlvl; |
oldlvl = shlvl; |
4532 |
shlvl++; |
shlvl++; |
4533 |
|
|
4534 |
|
/* man bash: "Non-builtin commands run by bash have signal handlers |
4535 |
|
* set to the values inherited by the shell from its parent". |
4536 |
|
* Do we do it correctly? */ |
4537 |
|
|
4538 |
closescript(); |
closescript(); |
4539 |
|
|
4540 |
|
if (mode == FORK_NOJOB /* is it `xxx` ? */ |
4541 |
|
&& n && n->type == NCMD /* is it single cmd? */ |
4542 |
|
/* && n->ncmd.args->type == NARG - always true? */ |
4543 |
|
&& n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0 |
4544 |
|
&& n->ncmd.args->narg.next == NULL /* "trap" with no arguments */ |
4545 |
|
/* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */ |
4546 |
|
) { |
4547 |
|
TRACE(("Trap hack\n")); |
4548 |
|
/* Awful hack for `trap` or $(trap). |
4549 |
|
* |
4550 |
|
* http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html |
4551 |
|
* contains an example where "trap" is executed in a subshell: |
4552 |
|
* |
4553 |
|
* save_traps=$(trap) |
4554 |
|
* ... |
4555 |
|
* eval "$save_traps" |
4556 |
|
* |
4557 |
|
* Standard does not say that "trap" in subshell shall print |
4558 |
|
* parent shell's traps. It only says that its output |
4559 |
|
* must have suitable form, but then, in the above example |
4560 |
|
* (which is not supposed to be normative), it implies that. |
4561 |
|
* |
4562 |
|
* bash (and probably other shell) does implement it |
4563 |
|
* (traps are reset to defaults, but "trap" still shows them), |
4564 |
|
* but as a result, "trap" logic is hopelessly messed up: |
4565 |
|
* |
4566 |
|
* # trap |
4567 |
|
* trap -- 'echo Ho' SIGWINCH <--- we have a handler |
4568 |
|
* # (trap) <--- trap is in subshell - no output (correct, traps are reset) |
4569 |
|
* # true | trap <--- trap is in subshell - no output (ditto) |
4570 |
|
* # echo `true | trap` <--- in subshell - output (but traps are reset!) |
4571 |
|
* trap -- 'echo Ho' SIGWINCH |
4572 |
|
* # echo `(trap)` <--- in subshell in subshell - output |
4573 |
|
* trap -- 'echo Ho' SIGWINCH |
4574 |
|
* # echo `true | (trap)` <--- in subshell in subshell in subshell - output! |
4575 |
|
* trap -- 'echo Ho' SIGWINCH |
4576 |
|
* |
4577 |
|
* The rules when to forget and when to not forget traps |
4578 |
|
* get really complex and nonsensical. |
4579 |
|
* |
4580 |
|
* Our solution: ONLY bare $(trap) or `trap` is special. |
4581 |
|
*/ |
4582 |
|
/* Save trap handler strings for trap builtin to print */ |
4583 |
|
trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap)); |
4584 |
|
/* Fall through into clearing traps */ |
4585 |
|
} |
4586 |
clear_traps(); |
clear_traps(); |
4587 |
#if JOBS |
#if JOBS |
4588 |
/* do job control only in root shell */ |
/* do job control only in root shell */ |
4593 |
if (jp->nprocs == 0) |
if (jp->nprocs == 0) |
4594 |
pgrp = getpid(); |
pgrp = getpid(); |
4595 |
else |
else |
4596 |
pgrp = jp->ps[0].pid; |
pgrp = jp->ps[0].ps_pid; |
4597 |
/* This can fail because we are doing it in the parent also */ |
/* this can fail because we are doing it in the parent also */ |
4598 |
(void)setpgid(0, pgrp); |
setpgid(0, pgrp); |
4599 |
if (mode == FORK_FG) |
if (mode == FORK_FG) |
4600 |
xtcsetpgrp(ttyfd, pgrp); |
xtcsetpgrp(ttyfd, pgrp); |
4601 |
setsignal(SIGTSTP); |
setsignal(SIGTSTP); |
4603 |
} else |
} else |
4604 |
#endif |
#endif |
4605 |
if (mode == FORK_BG) { |
if (mode == FORK_BG) { |
4606 |
|
/* man bash: "When job control is not in effect, |
4607 |
|
* asynchronous commands ignore SIGINT and SIGQUIT" */ |
4608 |
ignoresig(SIGINT); |
ignoresig(SIGINT); |
4609 |
ignoresig(SIGQUIT); |
ignoresig(SIGQUIT); |
4610 |
if (jp->nprocs == 0) { |
if (jp->nprocs == 0) { |
4611 |
close(0); |
close(0); |
4612 |
if (open(bb_dev_null, O_RDONLY) != 0) |
if (open(bb_dev_null, O_RDONLY) != 0) |
4613 |
ash_msg_and_raise_error("can't open %s", bb_dev_null); |
ash_msg_and_raise_error("can't open '%s'", bb_dev_null); |
4614 |
} |
} |
4615 |
} |
} |
4616 |
if (!oldlvl && iflag) { |
if (!oldlvl) { |
4617 |
setsignal(SIGINT); |
if (iflag) { /* why if iflag only? */ |
4618 |
|
setsignal(SIGINT); |
4619 |
|
setsignal(SIGTERM); |
4620 |
|
} |
4621 |
|
/* man bash: |
4622 |
|
* "In all cases, bash ignores SIGQUIT. Non-builtin |
4623 |
|
* commands run by bash have signal handlers |
4624 |
|
* set to the values inherited by the shell |
4625 |
|
* from its parent". |
4626 |
|
* Take care of the second rule: */ |
4627 |
setsignal(SIGQUIT); |
setsignal(SIGQUIT); |
|
setsignal(SIGTERM); |
|
4628 |
} |
} |
4629 |
|
#if JOBS |
4630 |
|
if (n && n->type == NCMD |
4631 |
|
&& n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0 |
4632 |
|
) { |
4633 |
|
TRACE(("Job hack\n")); |
4634 |
|
/* "jobs": we do not want to clear job list for it, |
4635 |
|
* instead we remove only _its_ own_ job from job list. |
4636 |
|
* This makes "jobs .... | cat" more useful. |
4637 |
|
*/ |
4638 |
|
freejob(curjob); |
4639 |
|
return; |
4640 |
|
} |
4641 |
|
#endif |
4642 |
for (jp = curjob; jp; jp = jp->prev_job) |
for (jp = curjob; jp; jp = jp->prev_job) |
4643 |
freejob(jp); |
freejob(jp); |
4644 |
jobless = 0; |
jobless = 0; |
4665 |
if (jp->nprocs == 0) |
if (jp->nprocs == 0) |
4666 |
pgrp = pid; |
pgrp = pid; |
4667 |
else |
else |
4668 |
pgrp = jp->ps[0].pid; |
pgrp = jp->ps[0].ps_pid; |
4669 |
/* This can fail because we are doing it in the child also */ |
/* This can fail because we are doing it in the child also */ |
4670 |
setpgid(pid, pgrp); |
setpgid(pid, pgrp); |
4671 |
} |
} |
4676 |
} |
} |
4677 |
if (jp) { |
if (jp) { |
4678 |
struct procstat *ps = &jp->ps[jp->nprocs++]; |
struct procstat *ps = &jp->ps[jp->nprocs++]; |
4679 |
ps->pid = pid; |
ps->ps_pid = pid; |
4680 |
ps->status = -1; |
ps->ps_status = -1; |
4681 |
ps->cmd = nullstr; |
ps->ps_cmd = nullstr; |
4682 |
#if JOBS |
#if JOBS |
4683 |
if (doing_jobctl && n) |
if (doing_jobctl && n) |
4684 |
ps->cmd = commandtext(n); |
ps->ps_cmd = commandtext(n); |
4685 |
#endif |
#endif |
4686 |
} |
} |
4687 |
} |
} |
4699 |
freejob(jp); |
freejob(jp); |
4700 |
ash_msg_and_raise_error("can't fork"); |
ash_msg_and_raise_error("can't fork"); |
4701 |
} |
} |
4702 |
if (pid == 0) |
if (pid == 0) { |
4703 |
forkchild(jp, /*n,*/ mode); |
CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */ |
4704 |
else |
forkchild(jp, n, mode); |
4705 |
|
} else { |
4706 |
forkparent(jp, n, mode, pid); |
forkparent(jp, n, mode, pid); |
4707 |
|
} |
4708 |
return pid; |
return pid; |
4709 |
} |
} |
4710 |
|
|
4711 |
/* |
/* |
4712 |
* Wait for job to finish. |
* Wait for job to finish. |
4713 |
* |
* |
4714 |
* Under job control we have the problem that while a child process is |
* Under job control we have the problem that while a child process |
4715 |
* running interrupts generated by the user are sent to the child but not |
* is running interrupts generated by the user are sent to the child |
4716 |
* to the shell. This means that an infinite loop started by an inter- |
* but not to the shell. This means that an infinite loop started by |
4717 |
* active user may be hard to kill. With job control turned off, an |
* an interactive user may be hard to kill. With job control turned off, |
4718 |
* interactive user may place an interactive program inside a loop. If |
* an interactive user may place an interactive program inside a loop. |
4719 |
* the interactive program catches interrupts, the user doesn't want |
* If the interactive program catches interrupts, the user doesn't want |
4720 |
* these interrupts to also abort the loop. The approach we take here |
* these interrupts to also abort the loop. The approach we take here |
4721 |
* is to have the shell ignore interrupt signals while waiting for a |
* is to have the shell ignore interrupt signals while waiting for a |
4722 |
* foreground process to terminate, and then send itself an interrupt |
* foreground process to terminate, and then send itself an interrupt |
4734 |
int st; |
int st; |
4735 |
|
|
4736 |
TRACE(("waitforjob(%%%d) called\n", jobno(jp))); |
TRACE(("waitforjob(%%%d) called\n", jobno(jp))); |
4737 |
|
|
4738 |
|
INT_OFF; |
4739 |
while (jp->state == JOBRUNNING) { |
while (jp->state == JOBRUNNING) { |
4740 |
|
/* In non-interactive shells, we _can_ get |
4741 |
|
* a keyboard signal here and be EINTRed, |
4742 |
|
* but we just loop back, waiting for command to complete. |
4743 |
|
* |
4744 |
|
* man bash: |
4745 |
|
* "If bash is waiting for a command to complete and receives |
4746 |
|
* a signal for which a trap has been set, the trap |
4747 |
|
* will not be executed until the command completes." |
4748 |
|
* |
4749 |
|
* Reality is that even if trap is not set, bash |
4750 |
|
* will not act on the signal until command completes. |
4751 |
|
* Try this. sleep5intoff.c: |
4752 |
|
* #include <signal.h> |
4753 |
|
* #include <unistd.h> |
4754 |
|
* int main() { |
4755 |
|
* sigset_t set; |
4756 |
|
* sigemptyset(&set); |
4757 |
|
* sigaddset(&set, SIGINT); |
4758 |
|
* sigaddset(&set, SIGQUIT); |
4759 |
|
* sigprocmask(SIG_BLOCK, &set, NULL); |
4760 |
|
* sleep(5); |
4761 |
|
* return 0; |
4762 |
|
* } |
4763 |
|
* $ bash -c './sleep5intoff; echo hi' |
4764 |
|
* ^C^C^C^C <--- pressing ^C once a second |
4765 |
|
* $ _ |
4766 |
|
* $ bash -c './sleep5intoff; echo hi' |
4767 |
|
* ^\^\^\^\hi <--- pressing ^\ (SIGQUIT) |
4768 |
|
* $ _ |
4769 |
|
*/ |
4770 |
dowait(DOWAIT_BLOCK, jp); |
dowait(DOWAIT_BLOCK, jp); |
4771 |
} |
} |
4772 |
|
INT_ON; |
4773 |
|
|
4774 |
st = getstatus(jp); |
st = getstatus(jp); |
4775 |
#if JOBS |
#if JOBS |
4776 |
if (jp->jobctl) { |
if (jp->jobctl) { |
4906 |
if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { |
if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { |
4907 |
/* child */ |
/* child */ |
4908 |
close(pip[0]); |
close(pip[0]); |
4909 |
signal(SIGINT, SIG_IGN); |
ignoresig(SIGINT); //signal(SIGINT, SIG_IGN); |
4910 |
signal(SIGQUIT, SIG_IGN); |
ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN); |
4911 |
signal(SIGHUP, SIG_IGN); |
ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN); |
4912 |
#ifdef SIGTSTP |
ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN); |
|
signal(SIGTSTP, SIG_IGN); |
|
|
#endif |
|
4913 |
signal(SIGPIPE, SIG_DFL); |
signal(SIGPIPE, SIG_DFL); |
4914 |
if (redir->type == NHERE) |
if (redir->type == NHERE) |
4915 |
full_write(pip[1], redir->nhere.doc->narg.text, len); |
full_write(pip[1], redir->nhere.doc->narg.text, len); |
5029 |
struct redirtab *next; |
struct redirtab *next; |
5030 |
int nullredirs; |
int nullredirs; |
5031 |
int pair_count; |
int pair_count; |
5032 |
struct two_fd_t two_fd[0]; |
struct two_fd_t two_fd[]; |
5033 |
}; |
}; |
5034 |
#define redirlist (G_var.redirlist) |
#define redirlist (G_var.redirlist) |
5035 |
|
|
5108 |
do { |
do { |
5109 |
sv_pos++; |
sv_pos++; |
5110 |
#if ENABLE_ASH_BASH_COMPAT |
#if ENABLE_ASH_BASH_COMPAT |
5111 |
if (redir->nfile.type == NTO2) |
if (tmp->nfile.type == NTO2) |
5112 |
sv_pos++; |
sv_pos++; |
5113 |
#endif |
#endif |
5114 |
tmp = tmp->nfile.next; |
tmp = tmp->nfile.next; |
5286 |
redirect(redir, flags); |
redirect(redir, flags); |
5287 |
} |
} |
5288 |
exception_handler = savehandler; |
exception_handler = savehandler; |
5289 |
if (err && exception != EXERROR) |
if (err && exception_type != EXERROR) |
5290 |
longjmp(exception_handler->loc, 1); |
longjmp(exception_handler->loc, 1); |
5291 |
RESTORE_INT(saveint); |
RESTORE_INT(saveint); |
5292 |
return err; |
return err; |
5298 |
* We have to deal with backquotes, shell variables, and file metacharacters. |
* We have to deal with backquotes, shell variables, and file metacharacters. |
5299 |
*/ |
*/ |
5300 |
|
|
5301 |
#if ENABLE_ASH_MATH_SUPPORT_64 |
#if ENABLE_SH_MATH_SUPPORT |
5302 |
typedef int64_t arith_t; |
static arith_t |
5303 |
#define arith_t_type long long |
ash_arith(const char *s) |
5304 |
#else |
{ |
5305 |
typedef long arith_t; |
arith_eval_hooks_t math_hooks; |
5306 |
#define arith_t_type long |
arith_t result; |
5307 |
#endif |
int errcode = 0; |
5308 |
|
|
5309 |
|
math_hooks.lookupvar = lookupvar; |
5310 |
|
math_hooks.setvar = setvar2; |
5311 |
|
math_hooks.endofname = endofname; |
5312 |
|
|
5313 |
|
INT_OFF; |
5314 |
|
result = arith(s, &errcode, &math_hooks); |
5315 |
|
if (errcode < 0) { |
5316 |
|
if (errcode == -3) |
5317 |
|
ash_msg_and_raise_error("exponent less than 0"); |
5318 |
|
if (errcode == -2) |
5319 |
|
ash_msg_and_raise_error("divide by zero"); |
5320 |
|
if (errcode == -5) |
5321 |
|
ash_msg_and_raise_error("expression recursion loop detected"); |
5322 |
|
raise_error_syntax(s); |
5323 |
|
} |
5324 |
|
INT_ON; |
5325 |
|
|
5326 |
#if ENABLE_ASH_MATH_SUPPORT |
return result; |
5327 |
static arith_t dash_arith(const char *); |
} |
|
static arith_t arith(const char *expr, int *perrcode); |
|
5328 |
#endif |
#endif |
5329 |
|
|
5330 |
/* |
/* |
5340 |
#define EXP_WORD 0x80 /* expand word in parameter expansion */ |
#define EXP_WORD 0x80 /* expand word in parameter expansion */ |
5341 |
#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */ |
#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */ |
5342 |
/* |
/* |
5343 |
* _rmescape() flags |
* rmescape() flags |
5344 |
*/ |
*/ |
5345 |
#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ |
#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ |
5346 |
#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ |
#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ |
5384 |
int len; |
int len; |
5385 |
|
|
5386 |
expdest = makestrspace(32, expdest); |
expdest = makestrspace(32, expdest); |
5387 |
#if ENABLE_ASH_MATH_SUPPORT_64 |
len = fmtstr(expdest, 32, arith_t_fmt, num); |
|
len = fmtstr(expdest, 32, "%lld", (long long) num); |
|
|
#else |
|
|
len = fmtstr(expdest, 32, "%ld", num); |
|
|
#endif |
|
5388 |
STADJUST(len, expdest); |
STADJUST(len, expdest); |
5389 |
return len; |
return len; |
5390 |
} |
} |
5394 |
{ |
{ |
5395 |
size_t esc = 0; |
size_t esc = 0; |
5396 |
|
|
5397 |
while (p > start && *--p == CTLESC) { |
while (p > start && (unsigned char)*--p == CTLESC) { |
5398 |
esc++; |
esc++; |
5399 |
} |
} |
5400 |
return esc; |
return esc; |
5404 |
* Remove any CTLESC characters from a string. |
* Remove any CTLESC characters from a string. |
5405 |
*/ |
*/ |
5406 |
static char * |
static char * |
5407 |
_rmescapes(char *str, int flag) |
rmescapes(char *str, int flag) |
5408 |
{ |
{ |
5409 |
static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' }; |
static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' }; |
5410 |
|
|
5411 |
char *p, *q, *r; |
char *p, *q, *r; |
5412 |
unsigned inquotes; |
unsigned inquotes; |
5413 |
int notescaped; |
unsigned protect_against_glob; |
5414 |
int globbing; |
unsigned globbing; |
5415 |
|
|
5416 |
p = strpbrk(str, qchars); |
p = strpbrk(str, qchars); |
5417 |
if (!p) { |
if (!p) |
5418 |
return str; |
return str; |
5419 |
} |
|
5420 |
q = p; |
q = p; |
5421 |
r = str; |
r = str; |
5422 |
if (flag & RMESCAPE_ALLOC) { |
if (flag & RMESCAPE_ALLOC) { |
5435 |
q = (char *)memcpy(q, str, len) + len; |
q = (char *)memcpy(q, str, len) + len; |
5436 |
} |
} |
5437 |
} |
} |
5438 |
|
|
5439 |
inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED; |
inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED; |
5440 |
globbing = flag & RMESCAPE_GLOB; |
globbing = flag & RMESCAPE_GLOB; |
5441 |
notescaped = globbing; |
protect_against_glob = globbing; |
5442 |
while (*p) { |
while (*p) { |
5443 |
if (*p == CTLQUOTEMARK) { |
if ((unsigned char)*p == CTLQUOTEMARK) { |
5444 |
|
// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0 |
5445 |
|
// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok? |
5446 |
|
// Note: both inquotes and protect_against_glob only affect whether |
5447 |
|
// CTLESC,<ch> gets converted to <ch> or to \<ch> |
5448 |
inquotes = ~inquotes; |
inquotes = ~inquotes; |
5449 |
p++; |
p++; |
5450 |
notescaped = globbing; |
protect_against_glob = globbing; |
5451 |
continue; |
continue; |
5452 |
} |
} |
5453 |
if (*p == '\\') { |
if (*p == '\\') { |
5454 |
/* naked back slash */ |
/* naked back slash */ |
5455 |
notescaped = 0; |
protect_against_glob = 0; |
5456 |
goto copy; |
goto copy; |
5457 |
} |
} |
5458 |
if (*p == CTLESC) { |
if ((unsigned char)*p == CTLESC) { |
5459 |
p++; |
p++; |
5460 |
if (notescaped && inquotes && *p != '/') { |
if (protect_against_glob && inquotes && *p != '/') { |
5461 |
*q++ = '\\'; |
*q++ = '\\'; |
5462 |
} |
} |
5463 |
} |
} |
5464 |
notescaped = globbing; |
protect_against_glob = globbing; |
5465 |
copy: |
copy: |
5466 |
*q++ = *p++; |
*q++ = *p++; |
5467 |
} |
} |
5472 |
} |
} |
5473 |
return r; |
return r; |
5474 |
} |
} |
|
#define rmescapes(p) _rmescapes((p), 0) |
|
|
|
|
5475 |
#define pmatch(a, b) !fnmatch((a), (b), 0) |
#define pmatch(a, b) !fnmatch((a), (b), 0) |
5476 |
|
|
5477 |
/* |
/* |
5486 |
if (quoted) { |
if (quoted) { |
5487 |
flag |= RMESCAPE_QUOTED; |
flag |= RMESCAPE_QUOTED; |
5488 |
} |
} |
5489 |
return _rmescapes((char *)pattern, flag); |
return rmescapes((char *)pattern, flag); |
5490 |
} |
} |
5491 |
|
|
5492 |
/* |
/* |
5497 |
{ |
{ |
5498 |
char *q = expdest; |
char *q = expdest; |
5499 |
|
|
5500 |
q = makestrspace(len * 2, q); |
q = makestrspace(quotes ? len * 2 : len, q); |
5501 |
|
|
5502 |
while (len--) { |
while (len--) { |
5503 |
int c = signed_char2int(*p++); |
unsigned char c = *p++; |
5504 |
if (!c) |
if (c == '\0') |
5505 |
continue; |
continue; |
5506 |
if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK)) |
if (quotes) { |
5507 |
USTPUTC(CTLESC, q); |
int n = SIT(c, syntax); |
5508 |
|
if (n == CCTL || n == CBACK) |
5509 |
|
USTPUTC(CTLESC, q); |
5510 |
|
} |
5511 |
USTPUTC(c, q); |
USTPUTC(c, q); |
5512 |
} |
} |
5513 |
|
|
5584 |
} |
} |
5585 |
|
|
5586 |
static char * |
static char * |
5587 |
exptilde(char *startp, char *p, int flag) |
exptilde(char *startp, char *p, int flags) |
5588 |
{ |
{ |
5589 |
char c; |
unsigned char c; |
5590 |
char *name; |
char *name; |
5591 |
struct passwd *pw; |
struct passwd *pw; |
5592 |
const char *home; |
const char *home; |
5593 |
int quotes = flag & (EXP_FULL | EXP_CASE); |
int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); |
5594 |
int startloc; |
int startloc; |
5595 |
|
|
5596 |
name = p + 1; |
name = p + 1; |
5602 |
case CTLQUOTEMARK: |
case CTLQUOTEMARK: |
5603 |
return startp; |
return startp; |
5604 |
case ':': |
case ':': |
5605 |
if (flag & EXP_VARTILDE) |
if (flags & EXP_VARTILDE) |
5606 |
goto done; |
goto done; |
5607 |
break; |
break; |
5608 |
case '/': |
case '/': |
5650 |
#define EV_EXIT 01 /* exit after evaluating tree */ |
#define EV_EXIT 01 /* exit after evaluating tree */ |
5651 |
static void evaltree(union node *, int); |
static void evaltree(union node *, int); |
5652 |
|
|
5653 |
static void |
static void FAST_FUNC |
5654 |
evalbackcmd(union node *n, struct backcmd *result) |
evalbackcmd(union node *n, struct backcmd *result) |
5655 |
{ |
{ |
5656 |
int saveherefd; |
int saveherefd; |
5659 |
result->buf = NULL; |
result->buf = NULL; |
5660 |
result->nleft = 0; |
result->nleft = 0; |
5661 |
result->jp = NULL; |
result->jp = NULL; |
5662 |
if (n == NULL) { |
if (n == NULL) |
5663 |
goto out; |
goto out; |
|
} |
|
5664 |
|
|
5665 |
saveherefd = herefd; |
saveherefd = herefd; |
5666 |
herefd = -1; |
herefd = -1; |
5754 |
stackblock() + startloc)); |
stackblock() + startloc)); |
5755 |
} |
} |
5756 |
|
|
5757 |
#if ENABLE_ASH_MATH_SUPPORT |
#if ENABLE_SH_MATH_SUPPORT |
5758 |
/* |
/* |
5759 |
* Expand arithmetic expression. Backup to start of expression, |
* Expand arithmetic expression. Backup to start of expression, |
5760 |
* evaluate, place result in (backed up) result, adjust string position. |
* evaluate, place result in (backed up) result, adjust string position. |
5767 |
int flag; |
int flag; |
5768 |
int len; |
int len; |
5769 |
|
|
5770 |
/* ifsfree(); */ |
/* ifsfree(); */ |
5771 |
|
|
5772 |
/* |
/* |
5773 |
* This routine is slightly over-complicated for |
* This routine is slightly over-complicated for |
5781 |
do { |
do { |
5782 |
int esc; |
int esc; |
5783 |
|
|
5784 |
while (*p != CTLARI) { |
while ((unsigned char)*p != CTLARI) { |
5785 |
p--; |
p--; |
5786 |
#if DEBUG |
#if DEBUG |
5787 |
if (p < start) { |
if (p < start) { |
5807 |
expdest = p; |
expdest = p; |
5808 |
|
|
5809 |
if (quotes) |
if (quotes) |
5810 |
rmescapes(p + 2); |
rmescapes(p + 2, 0); |
5811 |
|
|
5812 |
len = cvtnum(dash_arith(p + 2)); |
len = cvtnum(ash_arith(p + 2)); |
5813 |
|
|
5814 |
if (flag != '"') |
if (flag != '"') |
5815 |
recordregion(begoff, begoff + len, 0); |
recordregion(begoff, begoff + len, 0); |
5817 |
#endif |
#endif |
5818 |
|
|
5819 |
/* argstr needs it */ |
/* argstr needs it */ |
5820 |
static char *evalvar(char *p, int flag, struct strlist *var_str_list); |
static char *evalvar(char *p, int flags, struct strlist *var_str_list); |
5821 |
|
|
5822 |
/* |
/* |
5823 |
* Perform variable and command substitution. If EXP_FULL is set, output CTLESC |
* Perform variable and command substitution. If EXP_FULL is set, output CTLESC |
5829 |
* for correct expansion of "B=$A" word. |
* for correct expansion of "B=$A" word. |
5830 |
*/ |
*/ |
5831 |
static void |
static void |
5832 |
argstr(char *p, int flag, struct strlist *var_str_list) |
argstr(char *p, int flags, struct strlist *var_str_list) |
5833 |
{ |
{ |
5834 |
static const char spclchars[] ALIGN1 = { |
static const char spclchars[] ALIGN1 = { |
5835 |
'=', |
'=', |
5840 |
CTLVAR, |
CTLVAR, |
5841 |
CTLBACKQ, |
CTLBACKQ, |
5842 |
CTLBACKQ | CTLQUOTE, |
CTLBACKQ | CTLQUOTE, |
5843 |
#if ENABLE_ASH_MATH_SUPPORT |
#if ENABLE_SH_MATH_SUPPORT |
5844 |
CTLENDARI, |
CTLENDARI, |
5845 |
#endif |
#endif |
5846 |
0 |
'\0' |
5847 |
}; |
}; |
5848 |
const char *reject = spclchars; |
const char *reject = spclchars; |
5849 |
int c; |
int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ |
5850 |
int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ |
int breakall = flags & EXP_WORD; |
|
int breakall = flag & EXP_WORD; |
|
5851 |
int inquotes; |
int inquotes; |
5852 |
size_t length; |
size_t length; |
5853 |
int startloc; |
int startloc; |
5854 |
|
|
5855 |
if (!(flag & EXP_VARTILDE)) { |
if (!(flags & EXP_VARTILDE)) { |
5856 |
reject += 2; |
reject += 2; |
5857 |
} else if (flag & EXP_VARTILDE2) { |
} else if (flags & EXP_VARTILDE2) { |
5858 |
reject++; |
reject++; |
5859 |
} |
} |
5860 |
inquotes = 0; |
inquotes = 0; |
5861 |
length = 0; |
length = 0; |
5862 |
if (flag & EXP_TILDE) { |
if (flags & EXP_TILDE) { |
5863 |
char *q; |
char *q; |
5864 |
|
|
5865 |
flag &= ~EXP_TILDE; |
flags &= ~EXP_TILDE; |
5866 |
tilde: |
tilde: |
5867 |
q = p; |
q = p; |
5868 |
if (*q == CTLESC && (flag & EXP_QWORD)) |
if (*q == CTLESC && (flags & EXP_QWORD)) |
5869 |
q++; |
q++; |
5870 |
if (*q == '~') |
if (*q == '~') |
5871 |
p = exptilde(p, q, flag); |
p = exptilde(p, q, flags); |
5872 |
} |
} |
5873 |
start: |
start: |
5874 |
startloc = expdest - (char *)stackblock(); |
startloc = expdest - (char *)stackblock(); |
5875 |
for (;;) { |
for (;;) { |
5876 |
|
unsigned char c; |
5877 |
|
|
5878 |
length += strcspn(p + length, reject); |
length += strcspn(p + length, reject); |
5879 |
c = p[length]; |
c = p[length]; |
5880 |
if (c && (!(c & 0x80) |
if (c) { |
5881 |
#if ENABLE_ASH_MATH_SUPPORT |
if (!(c & 0x80) |
5882 |
|| c == CTLENDARI |
#if ENABLE_SH_MATH_SUPPORT |
5883 |
|
|| c == CTLENDARI |
5884 |
#endif |
#endif |
5885 |
)) { |
) { |
5886 |
/* c == '=' || c == ':' || c == CTLENDARI */ |
/* c == '=' || c == ':' || c == CTLENDARI */ |
5887 |
length++; |
length++; |
5888 |
|
} |
5889 |
} |
} |
5890 |
if (length > 0) { |
if (length > 0) { |
5891 |
int newloc; |
int newloc; |
5903 |
case '\0': |
case '\0': |
5904 |
goto breakloop; |
goto breakloop; |
5905 |
case '=': |
case '=': |
5906 |
if (flag & EXP_VARTILDE2) { |
if (flags & EXP_VARTILDE2) { |
5907 |
p--; |
p--; |
5908 |
continue; |
continue; |
5909 |
} |
} |
5910 |
flag |= EXP_VARTILDE2; |
flags |= EXP_VARTILDE2; |
5911 |
reject++; |
reject++; |
5912 |
/* fall through */ |
/* fall through */ |
5913 |
case ':': |
case ':': |
5926 |
goto breakloop; |
goto breakloop; |
5927 |
case CTLQUOTEMARK: |
case CTLQUOTEMARK: |
5928 |
/* "$@" syntax adherence hack */ |
/* "$@" syntax adherence hack */ |
5929 |
if ( |
if (!inquotes |
5930 |
!inquotes && |
&& memcmp(p, dolatstr, 4) == 0 |
5931 |
!memcmp(p, dolatstr, 4) && |
&& ( p[4] == CTLQUOTEMARK |
5932 |
(p[4] == CTLQUOTEMARK || ( |
|| (p[4] == CTLENDVAR && p[5] == CTLQUOTEMARK) |
5933 |
p[4] == CTLENDVAR && |
) |
|
p[5] == CTLQUOTEMARK |
|
|
)) |
|
5934 |
) { |
) { |
5935 |
p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1; |
p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1; |
5936 |
goto start; |
goto start; |
5937 |
} |
} |
5938 |
inquotes = !inquotes; |
inquotes = !inquotes; |
5948 |
length++; |
length++; |
5949 |
goto addquote; |
goto addquote; |
5950 |
case CTLVAR: |
case CTLVAR: |
5951 |
p = evalvar(p, flag, var_str_list); |
p = evalvar(p, flags, var_str_list); |
5952 |
goto start; |
goto start; |
5953 |
case CTLBACKQ: |
case CTLBACKQ: |
5954 |
c = 0; |
c = '\0'; |
5955 |
case CTLBACKQ|CTLQUOTE: |
case CTLBACKQ|CTLQUOTE: |
5956 |
expbackq(argbackq->n, c, quotes); |
expbackq(argbackq->n, c, quotes); |
5957 |
argbackq = argbackq->next; |
argbackq = argbackq->next; |
5958 |
goto start; |
goto start; |
5959 |
#if ENABLE_ASH_MATH_SUPPORT |
#if ENABLE_SH_MATH_SUPPORT |
5960 |
case CTLENDARI: |
case CTLENDARI: |
5961 |
p--; |
p--; |
5962 |
expari(quotes); |
expari(quotes); |
6031 |
*loc2 = c; |
*loc2 = c; |
6032 |
if (match) // if (!match) |
if (match) // if (!match) |
6033 |
return loc; |
return loc; |
6034 |
if (quotes && *loc == CTLESC) |
if (quotes && (unsigned char)*loc == CTLESC) |
6035 |
loc++; |
loc++; |
6036 |
loc++; |
loc++; |
6037 |
loc2++; |
loc2++; |
6083 |
tail = nullstr; |
tail = nullstr; |
6084 |
msg = "parameter not set"; |
msg = "parameter not set"; |
6085 |
if (umsg) { |
if (umsg) { |
6086 |
if (*end == CTLENDVAR) { |
if ((unsigned char)*end == CTLENDVAR) { |
6087 |
if (varflags & VSNUL) |
if (varflags & VSNUL) |
6088 |
tail = " or null"; |
tail = " or null"; |
6089 |
} else |
} else { |
6090 |
msg = umsg; |
msg = umsg; |
6091 |
|
} |
6092 |
} |
} |
6093 |
ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail); |
ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail); |
6094 |
} |
} |
6131 |
char *startp; |
char *startp; |
6132 |
char *loc; |
char *loc; |
6133 |
char *rmesc, *rmescend; |
char *rmesc, *rmescend; |
6134 |
USE_ASH_BASH_COMPAT(char *repl = NULL;) |
IF_ASH_BASH_COMPAT(char *repl = NULL;) |
6135 |
USE_ASH_BASH_COMPAT(char null = '\0';) |
IF_ASH_BASH_COMPAT(char null = '\0';) |
6136 |
USE_ASH_BASH_COMPAT(int pos, len, orig_len;) |
IF_ASH_BASH_COMPAT(int pos, len, orig_len;) |
6137 |
int saveherefd = herefd; |
int saveherefd = herefd; |
6138 |
int amount, workloc, resetloc; |
int amount, workloc, resetloc; |
6139 |
int zero; |
int zero; |
6157 |
#if ENABLE_ASH_BASH_COMPAT |
#if ENABLE_ASH_BASH_COMPAT |
6158 |
case VSSUBSTR: |
case VSSUBSTR: |
6159 |
loc = str = stackblock() + strloc; |
loc = str = stackblock() + strloc; |
6160 |
// TODO: number() instead? It does error checking... |
/* Read POS in ${var:POS:LEN} */ |
6161 |
pos = atoi(loc); |
pos = atoi(loc); /* number(loc) errors out on "1:4" */ |
6162 |
len = str - startp - 1; |
len = str - startp - 1; |
6163 |
|
|
6164 |
/* *loc != '\0', guaranteed by parser */ |
/* *loc != '\0', guaranteed by parser */ |
6165 |
if (quotes) { |
if (quotes) { |
6166 |
char *ptr; |
char *ptr; |
6167 |
|
|
6168 |
/* We must adjust the length by the number of escapes we find. */ |
/* Adjust the length by the number of escapes */ |
6169 |
for (ptr = startp; ptr < (str - 1); ptr++) { |
for (ptr = startp; ptr < (str - 1); ptr++) { |
6170 |
if (*ptr == CTLESC) { |
if ((unsigned char)*ptr == CTLESC) { |
6171 |
len--; |
len--; |
6172 |
ptr++; |
ptr++; |
6173 |
} |
} |
6176 |
orig_len = len; |
orig_len = len; |
6177 |
|
|
6178 |
if (*loc++ == ':') { |
if (*loc++ == ':') { |
6179 |
// TODO: number() instead? It does error checking... |
/* ${var::LEN} */ |
6180 |
len = atoi(loc); |
len = number(loc); |
6181 |
} else { |
} else { |
6182 |
|
/* Skip POS in ${var:POS:LEN} */ |
6183 |
len = orig_len; |
len = orig_len; |
6184 |
while (*loc && *loc != ':') |
while (*loc && *loc != ':') { |
6185 |
|
/* TODO? |
6186 |
|
* bash complains on: var=qwe; echo ${var:1a:123} |
6187 |
|
if (!isdigit(*loc)) |
6188 |
|
ash_msg_and_raise_error(msg_illnum, str); |
6189 |
|
*/ |
6190 |
loc++; |
loc++; |
6191 |
if (*loc++ == ':') |
} |
6192 |
// TODO: number() instead? It does error checking... |
if (*loc++ == ':') { |
6193 |
len = atoi(loc); |
len = number(loc); |
6194 |
|
} |
6195 |
} |
} |
6196 |
if (pos >= orig_len) { |
if (pos >= orig_len) { |
6197 |
pos = 0; |
pos = 0; |
6201 |
len = orig_len - pos; |
len = orig_len - pos; |
6202 |
|
|
6203 |
for (str = startp; pos; str++, pos--) { |
for (str = startp; pos; str++, pos--) { |
6204 |
if (quotes && *str == CTLESC) |
if (quotes && (unsigned char)*str == CTLESC) |
6205 |
str++; |
str++; |
6206 |
} |
} |
6207 |
for (loc = startp; len; len--) { |
for (loc = startp; len; len--) { |
6208 |
if (quotes && *str == CTLESC) |
if (quotes && (unsigned char)*str == CTLESC) |
6209 |
*loc++ = *str++; |
*loc++ = *str++; |
6210 |
*loc++ = *str++; |
*loc++ = *str++; |
6211 |
} |
} |
6226 |
* stack will need rebasing, and we'll need to remove our work |
* stack will need rebasing, and we'll need to remove our work |
6227 |
* areas each time |
* areas each time |
6228 |
*/ |
*/ |
6229 |
USE_ASH_BASH_COMPAT(restart:) |
IF_ASH_BASH_COMPAT(restart:) |
6230 |
|
|
6231 |
amount = expdest - ((char *)stackblock() + resetloc); |
amount = expdest - ((char *)stackblock() + resetloc); |
6232 |
STADJUST(-amount, expdest); |
STADJUST(-amount, expdest); |
6235 |
rmesc = startp; |
rmesc = startp; |
6236 |
rmescend = (char *)stackblock() + strloc; |
rmescend = (char *)stackblock() + strloc; |
6237 |
if (quotes) { |
if (quotes) { |
6238 |
rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); |
rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); |
6239 |
if (rmesc != startp) { |
if (rmesc != startp) { |
6240 |
rmescend = expdest; |
rmescend = expdest; |
6241 |
startp = (char *)stackblock() + startloc; |
startp = (char *)stackblock() + startloc; |
6269 |
/* No match, advance */ |
/* No match, advance */ |
6270 |
restart_detect = stackblock(); |
restart_detect = stackblock(); |
6271 |
STPUTC(*idx, expdest); |
STPUTC(*idx, expdest); |
6272 |
if (quotes && *idx == CTLESC) { |
if (quotes && (unsigned char)*idx == CTLESC) { |
6273 |
idx++; |
idx++; |
6274 |
len++; |
len++; |
6275 |
STPUTC(*idx, expdest); |
STPUTC(*idx, expdest); |
6284 |
|
|
6285 |
if (subtype == VSREPLACEALL) { |
if (subtype == VSREPLACEALL) { |
6286 |
while (idx < loc) { |
while (idx < loc) { |
6287 |
if (quotes && *idx == CTLESC) |
if (quotes && (unsigned char)*idx == CTLESC) |
6288 |
idx++; |
idx++; |
6289 |
idx++; |
idx++; |
6290 |
rmesc++; |
rmesc++; |
6291 |
} |
} |
6292 |
} else |
} else { |
6293 |
idx = loc; |
idx = loc; |
6294 |
|
} |
6295 |
|
|
6296 |
for (loc = repl; *loc; loc++) { |
for (loc = repl; *loc; loc++) { |
6297 |
restart_detect = stackblock(); |
restart_detect = stackblock(); |
6352 |
|
|
6353 |
/* |
/* |
6354 |
* Add the value of a specialized variable to the stack string. |
* Add the value of a specialized variable to the stack string. |
6355 |
|
* name parameter (examples): |
6356 |
|
* ash -c 'echo $1' name:'1=' |
6357 |
|
* ash -c 'echo $qwe' name:'qwe=' |
6358 |
|
* ash -c 'echo $$' name:'$=' |
6359 |
|
* ash -c 'echo ${$}' name:'$=' |
6360 |
|
* ash -c 'echo ${$##q}' name:'$=q' |
6361 |
|
* ash -c 'echo ${#$}' name:'$=' |
6362 |
|
* note: examples with bad shell syntax: |
6363 |
|
* ash -c 'echo ${#$1}' name:'$=1' |
6364 |
|
* ash -c 'echo ${#1#}' name:'1=#' |
6365 |
*/ |
*/ |
6366 |
static ssize_t |
static NOINLINE ssize_t |
6367 |
varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) |
varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) |
6368 |
{ |
{ |
6369 |
|
const char *p; |
6370 |
int num; |
int num; |
|
char *p; |
|
6371 |
int i; |
int i; |
|
int sep = 0; |
|
6372 |
int sepq = 0; |
int sepq = 0; |
6373 |
ssize_t len = 0; |
ssize_t len = 0; |
|
char **ap; |
|
|
int syntax; |
|
|
int quoted = varflags & VSQUOTE; |
|
6374 |
int subtype = varflags & VSTYPE; |
int subtype = varflags & VSTYPE; |
6375 |
int quotes = flags & (EXP_FULL | EXP_CASE); |
int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); |
6376 |
|
int quoted = varflags & VSQUOTE; |
6377 |
if (quoted && (flags & EXP_FULL)) |
int syntax = quoted ? DQSYNTAX : BASESYNTAX; |
|
sep = 1 << CHAR_BIT; |
|
6378 |
|
|
|
syntax = quoted ? DQSYNTAX : BASESYNTAX; |
|
6379 |
switch (*name) { |
switch (*name) { |
6380 |
case '$': |
case '$': |
6381 |
num = rootpid; |
num = rootpid; |
6392 |
return -1; |
return -1; |
6393 |
numvar: |
numvar: |
6394 |
len = cvtnum(num); |
len = cvtnum(num); |
6395 |
break; |
goto check_1char_name; |
6396 |
case '-': |
case '-': |
6397 |
p = makestrspace(NOPTS, expdest); |
expdest = makestrspace(NOPTS, expdest); |
6398 |
for (i = NOPTS - 1; i >= 0; i--) { |
for (i = NOPTS - 1; i >= 0; i--) { |
6399 |
if (optlist[i]) { |
if (optlist[i]) { |
6400 |
USTPUTC(optletters(i), p); |
USTPUTC(optletters(i), expdest); |
6401 |
len++; |
len++; |
6402 |
} |
} |
6403 |
} |
} |
6404 |
expdest = p; |
check_1char_name: |
6405 |
|
#if 0 |
6406 |
|
/* handles cases similar to ${#$1} */ |
6407 |
|
if (name[2] != '\0') |
6408 |
|
raise_error_syntax("bad substitution"); |
6409 |
|
#endif |
6410 |
break; |
break; |
6411 |
case '@': |
case '@': { |
6412 |
if (sep) |
char **ap; |
6413 |
|
int sep; |
6414 |
|
|
6415 |
|
if (quoted && (flags & EXP_FULL)) { |
6416 |
|
/* note: this is not meant as PEOF value */ |
6417 |
|
sep = 1 << CHAR_BIT; |
6418 |
goto param; |
goto param; |
6419 |
|
} |
6420 |
/* fall through */ |
/* fall through */ |
6421 |
case '*': |
case '*': |
6422 |
sep = ifsset() ? signed_char2int(ifsval()[0]) : ' '; |
sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' '; |
6423 |
if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK)) |
i = SIT(sep, syntax); |
6424 |
|
if (quotes && (i == CCTL || i == CBACK)) |
6425 |
sepq = 1; |
sepq = 1; |
6426 |
param: |
param: |
6427 |
ap = shellparam.p; |
ap = shellparam.p; |
6428 |
if (!ap) |
if (!ap) |
6429 |
return -1; |
return -1; |
6430 |
while ((p = *ap++)) { |
while ((p = *ap++) != NULL) { |
6431 |
size_t partlen; |
size_t partlen; |
6432 |
|
|
6433 |
partlen = strlen(p); |
partlen = strlen(p); |
6446 |
q = expdest; |
q = expdest; |
6447 |
if (sepq) |
if (sepq) |
6448 |
STPUTC(CTLESC, q); |
STPUTC(CTLESC, q); |
6449 |
|
/* note: may put NUL despite sep != 0 |
6450 |
|
* (see sep = 1 << CHAR_BIT above) */ |
6451 |
STPUTC(sep, q); |
STPUTC(sep, q); |
6452 |
expdest = q; |
expdest = q; |
6453 |
} |
} |
6454 |
} |
} |
6455 |
return len; |
return len; |
6456 |
|
} /* case '@' and '*' */ |
6457 |
case '0': |
case '0': |
6458 |
case '1': |
case '1': |
6459 |
case '2': |
case '2': |
6464 |
case '7': |
case '7': |
6465 |
case '8': |
case '8': |
6466 |
case '9': |
case '9': |
6467 |
// TODO: number() instead? It does error checking... |
num = atoi(name); /* number(name) fails on ${N#str} etc */ |
|
num = atoi(name); |
|
6468 |
if (num < 0 || num > shellparam.nparam) |
if (num < 0 || num > shellparam.nparam) |
6469 |
return -1; |
return -1; |
6470 |
p = num ? shellparam.p[num - 1] : arg0; |
p = num ? shellparam.p[num - 1] : arg0; |
6485 |
break; |
break; |
6486 |
eq++; |
eq++; |
6487 |
if (name_len == (unsigned)(eq - str) |
if (name_len == (unsigned)(eq - str) |
6488 |
&& strncmp(str, name, name_len) == 0) { |
&& strncmp(str, name, name_len) == 0 |
6489 |
|
) { |
6490 |
p = eq; |
p = eq; |
6491 |
/* goto value; - WRONG! */ |
/* goto value; - WRONG! */ |
6492 |
/* think "A=1 A=2 B=$A" */ |
/* think "A=1 A=2 B=$A" */ |
6517 |
* input string. |
* input string. |
6518 |
*/ |
*/ |
6519 |
static char * |
static char * |
6520 |
evalvar(char *p, int flag, struct strlist *var_str_list) |
evalvar(char *p, int flags, struct strlist *var_str_list) |
6521 |
{ |
{ |
6522 |
char varflags; |
char varflags; |
6523 |
char subtype; |
char subtype; |
6528 |
int startloc; |
int startloc; |
6529 |
ssize_t varlen; |
ssize_t varlen; |
6530 |
|
|
6531 |
varflags = *p++; |
varflags = (unsigned char) *p++; |
6532 |
subtype = varflags & VSTYPE; |
subtype = varflags & VSTYPE; |
6533 |
quoted = varflags & VSQUOTE; |
quoted = varflags & VSQUOTE; |
6534 |
var = p; |
var = p; |
6537 |
p = strchr(p, '=') + 1; |
p = strchr(p, '=') + 1; |
6538 |
|
|
6539 |
again: |
again: |
6540 |
varlen = varvalue(var, varflags, flag, var_str_list); |
varlen = varvalue(var, varflags, flags, var_str_list); |
6541 |
if (varflags & VSNUL) |
if (varflags & VSNUL) |
6542 |
varlen--; |
varlen--; |
6543 |
|
|
6550 |
vsplus: |
vsplus: |
6551 |
if (varlen < 0) { |
if (varlen < 0) { |
6552 |
argstr( |
argstr( |
6553 |
p, flag | EXP_TILDE | |
p, flags | EXP_TILDE | |
6554 |
(quoted ? EXP_QWORD : EXP_WORD), |
(quoted ? EXP_QWORD : EXP_WORD), |
6555 |
var_str_list |
var_str_list |
6556 |
); |
); |
6557 |
goto end; |
goto end; |
6623 |
patloc = expdest - (char *)stackblock(); |
patloc = expdest - (char *)stackblock(); |
6624 |
if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype, |
if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype, |
6625 |
startloc, varflags, |
startloc, varflags, |
6626 |
/* quotes: */ flag & (EXP_FULL | EXP_CASE), |
//TODO: | EXP_REDIR too? All other such places do it too |
6627 |
|
/* quotes: */ flags & (EXP_FULL | EXP_CASE), |
6628 |
var_str_list) |
var_str_list) |
6629 |
) { |
) { |
6630 |
int amount = expdest - ( |
int amount = expdest - ( |
6642 |
if (subtype != VSNORMAL) { /* skip to end of alternative */ |
if (subtype != VSNORMAL) { /* skip to end of alternative */ |
6643 |
int nesting = 1; |
int nesting = 1; |
6644 |
for (;;) { |
for (;;) { |
6645 |
char c = *p++; |
unsigned char c = *p++; |
6646 |
if (c == CTLESC) |
if (c == CTLESC) |
6647 |
p++; |
p++; |
6648 |
else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { |
else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { |
6690 |
ifsspc = 0; |
ifsspc = 0; |
6691 |
while (p < string + ifsp->endoff) { |
while (p < string + ifsp->endoff) { |
6692 |
q = p; |
q = p; |
6693 |
if (*p == CTLESC) |
if ((unsigned char)*p == CTLESC) |
6694 |
p++; |
p++; |
6695 |
if (!strchr(ifs, *p)) { |
if (!strchr(ifs, *p)) { |
6696 |
p++; |
p++; |
6716 |
break; |
break; |
6717 |
} |
} |
6718 |
q = p; |
q = p; |
6719 |
if (*p == CTLESC) |
if ((unsigned char)*p == CTLESC) |
6720 |
p++; |
p++; |
6721 |
if (strchr(ifs, *p) == NULL) { |
if (strchr(ifs, *p) == NULL) { |
6722 |
p = q; |
p = q; |
6878 |
p++; |
p++; |
6879 |
if (*p == '.') |
if (*p == '.') |
6880 |
matchdot++; |
matchdot++; |
6881 |
while (!intpending && (dp = readdir(dirp)) != NULL) { |
while (!pending_int && (dp = readdir(dirp)) != NULL) { |
6882 |
if (dp->d_name[0] == '.' && !matchdot) |
if (dp->d_name[0] == '.' && !matchdot) |
6883 |
continue; |
continue; |
6884 |
if (pmatch(start, dp->d_name)) { |
if (pmatch(start, dp->d_name)) { |
6999 |
*/ |
*/ |
7000 |
nometa: |
nometa: |
7001 |
*exparg.lastp = str; |
*exparg.lastp = str; |
7002 |
rmescapes(str->text); |
rmescapes(str->text, 0); |
7003 |
exparg.lastp = &str->next; |
exparg.lastp = &str->next; |
7004 |
} else { |
} else { |
7005 |
*exparg.lastp = NULL; |
*exparg.lastp = NULL; |
7047 |
expandmeta(exparg.list /*, flag*/); |
expandmeta(exparg.list /*, flag*/); |
7048 |
} else { |
} else { |
7049 |
if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ |
if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ |
7050 |
rmescapes(p); |
rmescapes(p, 0); |
7051 |
sp = stzalloc(sizeof(*sp)); |
sp = stzalloc(sizeof(*sp)); |
7052 |
sp->text = p; |
sp->text = p; |
7053 |
*exparg.lastp = sp; |
*exparg.lastp = sp; |
7108 |
|
|
7109 |
struct builtincmd { |
struct builtincmd { |
7110 |
const char *name; |
const char *name; |
7111 |
int (*builtin)(int, char **); |
int (*builtin)(int, char **) FAST_FUNC; |
7112 |
/* unsigned flags; */ |
/* unsigned flags; */ |
7113 |
}; |
}; |
7114 |
#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1) |
#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1) |
7173 |
|
|
7174 |
|
|
7175 |
static void |
static void |
7176 |
tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp) |
tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp) |
7177 |
{ |
{ |
7178 |
int repeated = 0; |
int repeated = 0; |
7179 |
|
|
7245 |
|| (applet_no = find_applet_by_name(argv[0])) >= 0 |
|| (applet_no = find_applet_by_name(argv[0])) >= 0 |
7246 |
#endif |
#endif |
7247 |
) { |
) { |
7248 |
tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp); |
tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp); |
7249 |
e = errno; |
e = errno; |
7250 |
} else { |
} else { |
7251 |
e = ENOENT; |
e = ENOENT; |
7252 |
while ((cmdname = padvance(&path, argv[0])) != NULL) { |
while ((cmdname = path_advance(&path, argv[0])) != NULL) { |
7253 |
if (--idx < 0 && pathopt == NULL) { |
if (--idx < 0 && pathopt == NULL) { |
7254 |
tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp); |
tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp); |
7255 |
if (errno != ENOENT && errno != ENOTDIR) |
if (errno != ENOENT && errno != ENOTDIR) |
7256 |
e = errno; |
e = errno; |
7257 |
} |
} |
7272 |
break; |
break; |
7273 |
} |
} |
7274 |
exitstatus = exerrno; |
exitstatus = exerrno; |
7275 |
TRACE(("shellexec failed for %s, errno %d, suppressint %d\n", |
TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n", |
7276 |
argv[0], e, suppressint)); |
argv[0], e, suppress_int)); |
7277 |
ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found")); |
ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found")); |
7278 |
/* NOTREACHED */ |
/* NOTREACHED */ |
7279 |
} |
} |
7288 |
idx = cmdp->param.index; |
idx = cmdp->param.index; |
7289 |
path = pathval(); |
path = pathval(); |
7290 |
do { |
do { |
7291 |
name = padvance(&path, cmdp->cmdname); |
name = path_advance(&path, cmdp->cmdname); |
7292 |
stunalloc(name); |
stunalloc(name); |
7293 |
} while (--idx >= 0); |
} while (--idx >= 0); |
7294 |
out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr)); |
out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr)); |
7402 |
cmdp->rehash = 0; |
cmdp->rehash = 0; |
7403 |
} |
} |
7404 |
|
|
7405 |
static int |
static int FAST_FUNC |
7406 |
hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
7407 |
{ |
{ |
7408 |
struct tblentry **pp; |
struct tblentry **pp; |
7472 |
* pathval() still returns the old value at this point. |
* pathval() still returns the old value at this point. |
7473 |
* Called with interrupts off. |
* Called with interrupts off. |
7474 |
*/ |
*/ |
7475 |
static void |
static void FAST_FUNC |
7476 |
changepath(const char *new) |
changepath(const char *new) |
7477 |
{ |
{ |
7478 |
const char *old; |
const char *old; |
7660 |
p = command; |
p = command; |
7661 |
} else { |
} else { |
7662 |
do { |
do { |
7663 |
p = padvance(&path, command); |
p = path_advance(&path, command); |
7664 |
stunalloc(p); |
stunalloc(p); |
7665 |
} while (--j >= 0); |
} while (--j >= 0); |
7666 |
} |
} |
7700 |
return 127; |
return 127; |
7701 |
} |
} |
7702 |
out: |
out: |
7703 |
outstr("\n", stdout); |
out1str("\n"); |
7704 |
return 0; |
return 0; |
7705 |
} |
} |
7706 |
|
|
7707 |
static int |
static int FAST_FUNC |
7708 |
typecmd(int argc UNUSED_PARAM, char **argv) |
typecmd(int argc UNUSED_PARAM, char **argv) |
7709 |
{ |
{ |
7710 |
int i = 1; |
int i = 1; |
7723 |
} |
} |
7724 |
|
|
7725 |
#if ENABLE_ASH_CMDCMD |
#if ENABLE_ASH_CMDCMD |
7726 |
static int |
static int FAST_FUNC |
7727 |
commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
7728 |
{ |
{ |
7729 |
int c; |
int c; |
7763 |
#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ |
#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ |
7764 |
#define EV_BACKCMD 04 /* command executing within back quotes */ |
#define EV_BACKCMD 04 /* command executing within back quotes */ |
7765 |
|
|
7766 |
static const short nodesize[N_NUMBER] = { |
static const uint8_t nodesize[N_NUMBER] = { |
7767 |
[NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), |
[NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), |
7768 |
[NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)), |
[NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)), |
7769 |
[NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)), |
[NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)), |
8046 |
INT_ON; |
INT_ON; |
8047 |
} |
} |
8048 |
|
|
8049 |
static int evalskip; /* set if we are skipping commands */ |
/* Reasons for skipping commands (see comment on breakcmd routine) */ |
|
/* reasons for skipping commands (see comment on breakcmd routine) */ |
|
8050 |
#define SKIPBREAK (1 << 0) |
#define SKIPBREAK (1 << 0) |
8051 |
#define SKIPCONT (1 << 1) |
#define SKIPCONT (1 << 1) |
8052 |
#define SKIPFUNC (1 << 2) |
#define SKIPFUNC (1 << 2) |
8053 |
#define SKIPFILE (1 << 3) |
#define SKIPFILE (1 << 3) |
8054 |
#define SKIPEVAL (1 << 4) |
#define SKIPEVAL (1 << 4) |
8055 |
|
static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ |
8056 |
static int skipcount; /* number of levels to skip */ |
static int skipcount; /* number of levels to skip */ |
8057 |
static int funcnest; /* depth of function calls */ |
static int funcnest; /* depth of function calls */ |
8058 |
static int loopnest; /* current loop nesting level */ |
static int loopnest; /* current loop nesting level */ |
8059 |
|
|
8060 |
/* forward decl way out to parsing code - dotrap needs it */ |
/* Forward decl way out to parsing code - dotrap needs it */ |
8061 |
static int evalstring(char *s, int mask); |
static int evalstring(char *s, int mask); |
8062 |
|
|
8063 |
/* |
/* Called to execute a trap. |
8064 |
* Called to execute a trap. Perhaps we should avoid entering new trap |
* Single callsite - at the end of evaltree(). |
8065 |
* handlers while we are executing a trap handler. |
* If we return non-zero, exaltree raises EXEXIT exception. |
8066 |
|
* |
8067 |
|
* Perhaps we should avoid entering new trap handlers |
8068 |
|
* while we are executing a trap handler. [is it a TODO?] |
8069 |
*/ |
*/ |
8070 |
static int |
static int |
8071 |
dotrap(void) |
dotrap(void) |
8072 |
{ |
{ |
8073 |
char *p; |
uint8_t *g; |
8074 |
char *q; |
int sig; |
8075 |
int i; |
uint8_t savestatus; |
|
int savestatus; |
|
|
int skip; |
|
8076 |
|
|
8077 |
savestatus = exitstatus; |
savestatus = exitstatus; |
8078 |
pendingsig = 0; |
pending_sig = 0; |
8079 |
xbarrier(); |
xbarrier(); |
8080 |
|
|
8081 |
TRACE(("dotrap entered\n")); |
TRACE(("dotrap entered\n")); |
8082 |
for (i = 1, q = gotsig; i < NSIG; i++, q++) { |
for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) { |
8083 |
if (!*q) |
int want_exexit; |
8084 |
continue; |
char *t; |
8085 |
|
|
8086 |
p = trap[i]; |
if (*g == 0) |
8087 |
|
continue; |
8088 |
|
t = trap[sig]; |
8089 |
/* non-trapped SIGINT is handled separately by raise_interrupt, |
/* non-trapped SIGINT is handled separately by raise_interrupt, |
8090 |
* don't upset it by resetting gotsig[SIGINT-1] */ |
* don't upset it by resetting gotsig[SIGINT-1] */ |
8091 |
if (i == SIGINT && !p) |
if (sig == SIGINT && !t) |
8092 |
continue; |
continue; |
8093 |
|
|
8094 |
TRACE(("sig %d is active, will run handler '%s'\n", i, p)); |
TRACE(("sig %d is active, will run handler '%s'\n", sig, t)); |
8095 |
*q = '\0'; |
*g = 0; |
8096 |
if (!p) |
if (!t) |
8097 |
continue; |
continue; |
8098 |
skip = evalstring(p, SKIPEVAL); |
want_exexit = evalstring(t, SKIPEVAL); |
8099 |
exitstatus = savestatus; |
exitstatus = savestatus; |
8100 |
if (skip) { |
if (want_exexit) { |
8101 |
TRACE(("dotrap returns %d\n", skip)); |
TRACE(("dotrap returns %d\n", want_exexit)); |
8102 |
return skip; |
return want_exexit; |
8103 |
} |
} |
8104 |
} |
} |
8105 |
|
|
8138 |
TRACE(("evaltree(NULL) called\n")); |
TRACE(("evaltree(NULL) called\n")); |
8139 |
goto out1; |
goto out1; |
8140 |
} |
} |
8141 |
TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags)); |
TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags)); |
8142 |
|
|
8143 |
exception_handler = &jmploc; |
exception_handler = &jmploc; |
8144 |
{ |
{ |
8145 |
int err = setjmp(jmploc.loc); |
int err = setjmp(jmploc.loc); |
8146 |
if (err) { |
if (err) { |
8147 |
/* if it was a signal, check for trap handlers */ |
/* if it was a signal, check for trap handlers */ |
8148 |
if (exception == EXSIG) { |
if (exception_type == EXSIG) { |
8149 |
TRACE(("exception %d (EXSIG) in evaltree, err=%d\n", exception, err)); |
TRACE(("exception %d (EXSIG) in evaltree, err=%d\n", |
8150 |
|
exception_type, err)); |
8151 |
goto out; |
goto out; |
8152 |
} |
} |
8153 |
/* continue on the way out */ |
/* continue on the way out */ |
8154 |
TRACE(("exception %d in evaltree, propagating err=%d\n", exception, err)); |
TRACE(("exception %d in evaltree, propagating err=%d\n", |
8155 |
|
exception_type, err)); |
8156 |
exception_handler = savehandler; |
exception_handler = savehandler; |
8157 |
longjmp(exception_handler->loc, err); |
longjmp(exception_handler->loc, err); |
8158 |
} |
} |
8162 |
default: |
default: |
8163 |
#if DEBUG |
#if DEBUG |
8164 |
out1fmt("Node type = %d\n", n->type); |
out1fmt("Node type = %d\n", n->type); |
8165 |
fflush(stdout); |
fflush_all(); |
8166 |
break; |
break; |
8167 |
#endif |
#endif |
8168 |
case NNOT: |
case NNOT: |
8255 |
out1: |
out1: |
8256 |
if (checkexit & exitstatus) |
if (checkexit & exitstatus) |
8257 |
evalskip |= SKIPEVAL; |
evalskip |= SKIPEVAL; |
8258 |
else if (pendingsig && dotrap()) |
else if (pending_sig && dotrap()) |
8259 |
goto exexit; |
goto exexit; |
8260 |
|
|
8261 |
if (flags & EV_EXIT) { |
if (flags & EV_EXIT) { |
8541 |
static smallint did_banner; |
static smallint did_banner; |
8542 |
|
|
8543 |
if (!did_banner) { |
if (!did_banner) { |
8544 |
out1fmt( |
/* note: ash and hush share this string */ |
8545 |
"\n\n" |
out1fmt("\n\n%s %s\n" |
|
"%s built-in shell (ash)\n" |
|
8546 |
"Enter 'help' for a list of built-in commands." |
"Enter 'help' for a list of built-in commands." |
8547 |
"\n\n", |
"\n\n", |
8548 |
bb_banner); |
bb_banner, |
8549 |
|
"built-in shell (ash)" |
8550 |
|
); |
8551 |
did_banner = 1; |
did_banner = 1; |
8552 |
} |
} |
8553 |
} |
} |
8587 |
while ((lvp = localvars) != NULL) { |
while ((lvp = localvars) != NULL) { |
8588 |
localvars = lvp->next; |
localvars = lvp->next; |
8589 |
vp = lvp->vp; |
vp = lvp->vp; |
8590 |
TRACE(("poplocalvar %s", vp ? vp->text : "-")); |
TRACE(("poplocalvar %s\n", vp ? vp->text : "-")); |
8591 |
if (vp == NULL) { /* $- saved */ |
if (vp == NULL) { /* $- saved */ |
8592 |
memcpy(optlist, lvp->text, sizeof(optlist)); |
memcpy(optlist, lvp->text, sizeof(optlist)); |
8593 |
free((char*)lvp->text); |
free((char*)lvp->text); |
8735 |
/* |
/* |
8736 |
* The "local" command. |
* The "local" command. |
8737 |
*/ |
*/ |
8738 |
static int |
static int FAST_FUNC |
8739 |
localcmd(int argc UNUSED_PARAM, char **argv) |
localcmd(int argc UNUSED_PARAM, char **argv) |
8740 |
{ |
{ |
8741 |
char *name; |
char *name; |
8747 |
return 0; |
return 0; |
8748 |
} |
} |
8749 |
|
|
8750 |
static int |
static int FAST_FUNC |
8751 |
falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
8752 |
{ |
{ |
8753 |
return 1; |
return 1; |
8754 |
} |
} |
8755 |
|
|
8756 |
static int |
static int FAST_FUNC |
8757 |
truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
8758 |
{ |
{ |
8759 |
return 0; |
return 0; |
8760 |
} |
} |
8761 |
|
|
8762 |
static int |
static int FAST_FUNC |
8763 |
execcmd(int argc UNUSED_PARAM, char **argv) |
execcmd(int argc UNUSED_PARAM, char **argv) |
8764 |
{ |
{ |
8765 |
if (argv[1]) { |
if (argv[1]) { |
8774 |
/* |
/* |
8775 |
* The return command. |
* The return command. |
8776 |
*/ |
*/ |
8777 |
static int |
static int FAST_FUNC |
8778 |
returncmd(int argc UNUSED_PARAM, char **argv) |
returncmd(int argc UNUSED_PARAM, char **argv) |
8779 |
{ |
{ |
8780 |
/* |
/* |
8786 |
} |
} |
8787 |
|
|
8788 |
/* Forward declarations for builtintab[] */ |
/* Forward declarations for builtintab[] */ |
8789 |
static int breakcmd(int, char **); |
static int breakcmd(int, char **) FAST_FUNC; |
8790 |
static int dotcmd(int, char **); |
static int dotcmd(int, char **) FAST_FUNC; |
8791 |
static int evalcmd(int, char **); |
static int evalcmd(int, char **) FAST_FUNC; |
8792 |
static int exitcmd(int, char **); |
static int exitcmd(int, char **) FAST_FUNC; |
8793 |
static int exportcmd(int, char **); |
static int exportcmd(int, char **) FAST_FUNC; |
8794 |
#if ENABLE_ASH_GETOPTS |
#if ENABLE_ASH_GETOPTS |
8795 |
static int getoptscmd(int, char **); |
static int getoptscmd(int, char **) FAST_FUNC; |
8796 |
#endif |
#endif |
8797 |
#if !ENABLE_FEATURE_SH_EXTRA_QUIET |
#if !ENABLE_FEATURE_SH_EXTRA_QUIET |
8798 |
static int helpcmd(int, char **); |
static int helpcmd(int, char **) FAST_FUNC; |
8799 |
#endif |
#endif |
8800 |
#if ENABLE_ASH_MATH_SUPPORT |
#if ENABLE_SH_MATH_SUPPORT |
8801 |
static int letcmd(int, char **); |
static int letcmd(int, char **) FAST_FUNC; |
8802 |
#endif |
#endif |
8803 |
static int readcmd(int, char **); |
static int readcmd(int, char **) FAST_FUNC; |
8804 |
static int setcmd(int, char **); |
static int setcmd(int, char **) FAST_FUNC; |
8805 |
static int shiftcmd(int, char **); |
static int shiftcmd(int, char **) FAST_FUNC; |
8806 |
static int timescmd(int, char **); |
static int timescmd(int, char **) FAST_FUNC; |
8807 |
static int trapcmd(int, char **); |
static int trapcmd(int, char **) FAST_FUNC; |
8808 |
static int umaskcmd(int, char **); |
static int umaskcmd(int, char **) FAST_FUNC; |
8809 |
static int unsetcmd(int, char **); |
static int unsetcmd(int, char **) FAST_FUNC; |
8810 |
static int ulimitcmd(int, char **); |
static int ulimitcmd(int, char **) FAST_FUNC; |
8811 |
|
|
8812 |
#define BUILTIN_NOSPEC "0" |
#define BUILTIN_NOSPEC "0" |
8813 |
#define BUILTIN_SPECIAL "1" |
#define BUILTIN_SPECIAL "1" |
8818 |
#define BUILTIN_REG_ASSG "6" |
#define BUILTIN_REG_ASSG "6" |
8819 |
#define BUILTIN_SPEC_REG_ASSG "7" |
#define BUILTIN_SPEC_REG_ASSG "7" |
8820 |
|
|
8821 |
/* We do not handle [[ expr ]] bashism bash-compatibly, |
/* Stubs for calling non-FAST_FUNC's */ |
8822 |
* we make it a synonym of [ expr ]. |
#if ENABLE_ASH_BUILTIN_ECHO |
8823 |
* Basically, word splitting and pathname expansion should NOT be performed |
static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); } |
8824 |
* Examples: |
#endif |
8825 |
* no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0" |
#if ENABLE_ASH_BUILTIN_PRINTF |
8826 |
* no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0" |
static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); } |
8827 |
* Additional operators: |
#endif |
8828 |
* || and && should work as -o and -a |
#if ENABLE_ASH_BUILTIN_TEST |
8829 |
* =~ regexp match |
static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); } |
8830 |
* Apart from the above, [[ expr ]] should work as [ expr ] |
#endif |
|
*/ |
|
|
|
|
|
#define echocmd echo_main |
|
|
#define printfcmd printf_main |
|
|
#define testcmd test_main |
|
8831 |
|
|
8832 |
/* Keep these in proper order since it is searched via bsearch() */ |
/* Keep these in proper order since it is searched via bsearch() */ |
8833 |
static const struct builtincmd builtintab[] = { |
static const struct builtincmd builtintab[] = { |
8874 |
{ BUILTIN_REGULAR "jobs", jobscmd }, |
{ BUILTIN_REGULAR "jobs", jobscmd }, |
8875 |
{ BUILTIN_REGULAR "kill", killcmd }, |
{ BUILTIN_REGULAR "kill", killcmd }, |
8876 |
#endif |
#endif |
8877 |
#if ENABLE_ASH_MATH_SUPPORT |
#if ENABLE_SH_MATH_SUPPORT |
8878 |
{ BUILTIN_NOSPEC "let", letcmd }, |
{ BUILTIN_NOSPEC "let", letcmd }, |
8879 |
#endif |
#endif |
8880 |
{ BUILTIN_ASSIGN "local", localcmd }, |
{ BUILTIN_ASSIGN "local", localcmd }, |
8950 |
return 0; |
return 0; |
8951 |
return *q == '='; |
return *q == '='; |
8952 |
} |
} |
8953 |
static int |
static int FAST_FUNC |
8954 |
bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
8955 |
{ |
{ |
8956 |
/* Preserve exitstatus of a previous possible redirection |
/* Preserve exitstatus of a previous possible redirection |
9079 |
for (;;) { |
for (;;) { |
9080 |
find_command(argv[0], &cmdentry, cmd_flag, path); |
find_command(argv[0], &cmdentry, cmd_flag, path); |
9081 |
if (cmdentry.cmdtype == CMDUNKNOWN) { |
if (cmdentry.cmdtype == CMDUNKNOWN) { |
9082 |
flush_stderr(); |
flush_stdout_stderr(); |
9083 |
status = 127; |
status = 127; |
9084 |
goto bail; |
goto bail; |
9085 |
} |
} |
9169 |
|
|
9170 |
if (evalbltin(cmdentry.u.cmd, argc, argv)) { |
if (evalbltin(cmdentry.u.cmd, argc, argv)) { |
9171 |
int exit_status; |
int exit_status; |
9172 |
int i = exception; |
int i = exception_type; |
9173 |
if (i == EXEXIT) |
if (i == EXEXIT) |
9174 |
goto raise; |
goto raise; |
9175 |
exit_status = 2; |
exit_status = 2; |
9176 |
if (i == EXINT) |
if (i == EXINT) |
9177 |
exit_status = 128 + SIGINT; |
exit_status = 128 + SIGINT; |
9178 |
if (i == EXSIG) |
if (i == EXSIG) |
9179 |
exit_status = 128 + pendingsig; |
exit_status = 128 + pending_sig; |
9180 |
exitstatus = exit_status; |
exitstatus = exit_status; |
9181 |
if (i == EXINT || spclbltin > 0) { |
if (i == EXINT || spclbltin > 0) { |
9182 |
raise: |
raise: |
9230 |
exitstatus |= ferror(stdout); |
exitstatus |= ferror(stdout); |
9231 |
clearerr(stdout); |
clearerr(stdout); |
9232 |
commandname = savecmdname; |
commandname = savecmdname; |
|
// exsig = 0; |
|
9233 |
exception_handler = savehandler; |
exception_handler = savehandler; |
9234 |
|
|
9235 |
return i; |
return i; |
9274 |
* be an error to break out of more loops than exist, but it isn't |
* be an error to break out of more loops than exist, but it isn't |
9275 |
* in the standard shell so we don't make it one here. |
* in the standard shell so we don't make it one here. |
9276 |
*/ |
*/ |
9277 |
static int |
static int FAST_FUNC |
9278 |
breakcmd(int argc UNUSED_PARAM, char **argv) |
breakcmd(int argc UNUSED_PARAM, char **argv) |
9279 |
{ |
{ |
9280 |
int n = argv[1] ? number(argv[1]) : 1; |
int n = argv[1] ? number(argv[1]) : 1; |
9281 |
|
|
9282 |
if (n <= 0) |
if (n <= 0) |
9283 |
ash_msg_and_raise_error(illnum, argv[1]); |
ash_msg_and_raise_error(msg_illnum, argv[1]); |
9284 |
if (n > loopnest) |
if (n > loopnest) |
9285 |
n = loopnest; |
n = loopnest; |
9286 |
if (n > 0) { |
if (n > 0) { |
9301 |
INPUT_NOFILE_OK = 2, |
INPUT_NOFILE_OK = 2, |
9302 |
}; |
}; |
9303 |
|
|
|
static int plinno = 1; /* input line number */ |
|
|
/* number of characters left in input buffer */ |
|
|
static int parsenleft; /* copy of parsefile->nleft */ |
|
|
static int parselleft; /* copy of parsefile->lleft */ |
|
|
/* next character in input buffer */ |
|
|
static char *parsenextc; /* copy of parsefile->nextc */ |
|
|
|
|
9304 |
static smallint checkkwd; |
static smallint checkkwd; |
9305 |
/* values of checkkwd variable */ |
/* values of checkkwd variable */ |
9306 |
#define CHKALIAS 0x1 |
#define CHKALIAS 0x1 |
9307 |
#define CHKKWD 0x2 |
#define CHKKWD 0x2 |
9308 |
#define CHKNL 0x4 |
#define CHKNL 0x4 |
9309 |
|
|
9310 |
|
/* |
9311 |
|
* Push a string back onto the input at this current parsefile level. |
9312 |
|
* We handle aliases this way. |
9313 |
|
*/ |
9314 |
|
#if !ENABLE_ASH_ALIAS |
9315 |
|
#define pushstring(s, ap) pushstring(s) |
9316 |
|
#endif |
9317 |
|
static void |
9318 |
|
pushstring(char *s, struct alias *ap) |
9319 |
|
{ |
9320 |
|
struct strpush *sp; |
9321 |
|
int len; |
9322 |
|
|
9323 |
|
len = strlen(s); |
9324 |
|
INT_OFF; |
9325 |
|
if (g_parsefile->strpush) { |
9326 |
|
sp = ckzalloc(sizeof(*sp)); |
9327 |
|
sp->prev = g_parsefile->strpush; |
9328 |
|
} else { |
9329 |
|
sp = &(g_parsefile->basestrpush); |
9330 |
|
} |
9331 |
|
g_parsefile->strpush = sp; |
9332 |
|
sp->prev_string = g_parsefile->next_to_pgetc; |
9333 |
|
sp->prev_left_in_line = g_parsefile->left_in_line; |
9334 |
|
#if ENABLE_ASH_ALIAS |
9335 |
|
sp->ap = ap; |
9336 |
|
if (ap) { |
9337 |
|
ap->flag |= ALIASINUSE; |
9338 |
|
sp->string = s; |
9339 |
|
} |
9340 |
|
#endif |
9341 |
|
g_parsefile->next_to_pgetc = s; |
9342 |
|
g_parsefile->left_in_line = len; |
9343 |
|
INT_ON; |
9344 |
|
} |
9345 |
|
|
9346 |
static void |
static void |
9347 |
popstring(void) |
popstring(void) |
9348 |
{ |
{ |
9351 |
INT_OFF; |
INT_OFF; |
9352 |
#if ENABLE_ASH_ALIAS |
#if ENABLE_ASH_ALIAS |
9353 |
if (sp->ap) { |
if (sp->ap) { |
9354 |
if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') { |
if (g_parsefile->next_to_pgetc[-1] == ' ' |
9355 |
|
|| g_parsefile->next_to_pgetc[-1] == '\t' |
9356 |
|
) { |
9357 |
checkkwd |= CHKALIAS; |
checkkwd |= CHKALIAS; |
9358 |
} |
} |
9359 |
if (sp->string != sp->ap->val) { |
if (sp->string != sp->ap->val) { |
9365 |
} |
} |
9366 |
} |
} |
9367 |
#endif |
#endif |
9368 |
parsenextc = sp->prevstring; |
g_parsefile->next_to_pgetc = sp->prev_string; |
9369 |
parsenleft = sp->prevnleft; |
g_parsefile->left_in_line = sp->prev_left_in_line; |
9370 |
g_parsefile->strpush = sp->prev; |
g_parsefile->strpush = sp->prev; |
9371 |
if (sp != &(g_parsefile->basestrpush)) |
if (sp != &(g_parsefile->basestrpush)) |
9372 |
free(sp); |
free(sp); |
9373 |
INT_ON; |
INT_ON; |
9374 |
} |
} |
9375 |
|
|
9376 |
|
//FIXME: BASH_COMPAT with "...&" does TWO pungetc(): |
9377 |
|
//it peeks whether it is &>, and then pushes back both chars. |
9378 |
|
//This function needs to save last *next_to_pgetc to buf[0] |
9379 |
|
//to make two pungetc() reliable. Currently, |
9380 |
|
// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work... |
9381 |
static int |
static int |
9382 |
preadfd(void) |
preadfd(void) |
9383 |
{ |
{ |
9384 |
int nr; |
int nr; |
9385 |
char *buf = g_parsefile->buf; |
char *buf = g_parsefile->buf; |
|
parsenextc = buf; |
|
9386 |
|
|
9387 |
|
g_parsefile->next_to_pgetc = buf; |
9388 |
#if ENABLE_FEATURE_EDITING |
#if ENABLE_FEATURE_EDITING |
9389 |
retry: |
retry: |
9390 |
if (!iflag || g_parsefile->fd != STDIN_FILENO) |
if (!iflag || g_parsefile->fd != STDIN_FILENO) |
9435 |
* Refill the input buffer and return the next input character: |
* Refill the input buffer and return the next input character: |
9436 |
* |
* |
9437 |
* 1) If a string was pushed back on the input, pop it; |
* 1) If a string was pushed back on the input, pop it; |
9438 |
* 2) If an EOF was pushed back (parsenleft < -BIGNUM) or we are reading |
* 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM) |
9439 |
* from a string so we can't refill the buffer, return EOF. |
* or we are reading from a string so we can't refill the buffer, |
9440 |
* 3) If the is more stuff in this buffer, use it else call read to fill it. |
* return EOF. |
9441 |
|
* 3) If there is more stuff in this buffer, use it else call read to fill it. |
9442 |
* 4) Process input up to the next newline, deleting nul characters. |
* 4) Process input up to the next newline, deleting nul characters. |
9443 |
*/ |
*/ |
9444 |
//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__) |
//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__) |
9451 |
|
|
9452 |
while (g_parsefile->strpush) { |
while (g_parsefile->strpush) { |
9453 |
#if ENABLE_ASH_ALIAS |
#if ENABLE_ASH_ALIAS |
9454 |
if (parsenleft == -1 && g_parsefile->strpush->ap |
if (g_parsefile->left_in_line == -1 |
9455 |
&& parsenextc[-1] != ' ' && parsenextc[-1] != '\t' |
&& g_parsefile->strpush->ap |
9456 |
|
&& g_parsefile->next_to_pgetc[-1] != ' ' |
9457 |
|
&& g_parsefile->next_to_pgetc[-1] != '\t' |
9458 |
) { |
) { |
9459 |
pgetc_debug("preadbuffer PEOA"); |
pgetc_debug("preadbuffer PEOA"); |
9460 |
return PEOA; |
return PEOA; |
9462 |
#endif |
#endif |
9463 |
popstring(); |
popstring(); |
9464 |
/* try "pgetc" now: */ |
/* try "pgetc" now: */ |
9465 |
pgetc_debug("internal pgetc at %d:%p'%s'", parsenleft, parsenextc, parsenextc); |
pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'", |
9466 |
if (--parsenleft >= 0) |
g_parsefile->left_in_line, |
9467 |
return signed_char2int(*parsenextc++); |
g_parsefile->next_to_pgetc, |
9468 |
|
g_parsefile->next_to_pgetc); |
9469 |
|
if (--g_parsefile->left_in_line >= 0) |
9470 |
|
return (unsigned char)(*g_parsefile->next_to_pgetc++); |
9471 |
} |
} |
9472 |
/* on both branches above parsenleft < 0. |
/* on both branches above g_parsefile->left_in_line < 0. |
9473 |
* "pgetc" needs refilling. |
* "pgetc" needs refilling. |
9474 |
*/ |
*/ |
9475 |
|
|
9476 |
/* -90 is -BIGNUM. Below we use -99 to mark "EOF on read", |
/* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read", |
9477 |
* pungetc() may decrement it a few times. -90 is enough. |
* pungetc() may increment it a few times. |
9478 |
|
* Assuming it won't increment it to less than -90. |
9479 |
*/ |
*/ |
9480 |
if (parsenleft < -90 || g_parsefile->buf == NULL) { |
if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) { |
9481 |
pgetc_debug("preadbuffer PEOF1"); |
pgetc_debug("preadbuffer PEOF1"); |
9482 |
/* even in failure keep them in lock step, |
/* even in failure keep left_in_line and next_to_pgetc |
9483 |
* for correct pungetc. */ |
* in lock step, for correct multi-layer pungetc. |
9484 |
parsenextc++; |
* left_in_line was decremented before preadbuffer(), |
9485 |
|
* must inc next_to_pgetc: */ |
9486 |
|
g_parsefile->next_to_pgetc++; |
9487 |
return PEOF; |
return PEOF; |
9488 |
} |
} |
9489 |
|
|
9490 |
more = parselleft; |
more = g_parsefile->left_in_buffer; |
9491 |
if (more <= 0) { |
if (more <= 0) { |
9492 |
flush_stdout_stderr(); |
flush_stdout_stderr(); |
9493 |
again: |
again: |
9494 |
more = preadfd(); |
more = preadfd(); |
9495 |
if (more <= 0) { |
if (more <= 0) { |
9496 |
parselleft = parsenleft = -99; |
/* don't try reading again */ |
9497 |
|
g_parsefile->left_in_line = -99; |
9498 |
pgetc_debug("preadbuffer PEOF2"); |
pgetc_debug("preadbuffer PEOF2"); |
9499 |
parsenextc++; |
g_parsefile->next_to_pgetc++; |
9500 |
return PEOF; |
return PEOF; |
9501 |
} |
} |
9502 |
} |
} |
9503 |
|
|
9504 |
/* Find out where's the end of line. |
/* Find out where's the end of line. |
9505 |
* Set parsenleft/parselleft acordingly. |
* Set g_parsefile->left_in_line |
9506 |
|
* and g_parsefile->left_in_buffer acordingly. |
9507 |
* NUL chars are deleted. |
* NUL chars are deleted. |
9508 |
*/ |
*/ |
9509 |
q = parsenextc; |
q = g_parsefile->next_to_pgetc; |
9510 |
for (;;) { |
for (;;) { |
9511 |
char c; |
char c; |
9512 |
|
|
9518 |
} else { |
} else { |
9519 |
q++; |
q++; |
9520 |
if (c == '\n') { |
if (c == '\n') { |
9521 |
parsenleft = q - parsenextc - 1; |
g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1; |
9522 |
break; |
break; |
9523 |
} |
} |
9524 |
} |
} |
9525 |
|
|
9526 |
if (more <= 0) { |
if (more <= 0) { |
9527 |
parsenleft = q - parsenextc - 1; |
g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1; |
9528 |
if (parsenleft < 0) |
if (g_parsefile->left_in_line < 0) |
9529 |
goto again; |
goto again; |
9530 |
break; |
break; |
9531 |
} |
} |
9532 |
} |
} |
9533 |
parselleft = more; |
g_parsefile->left_in_buffer = more; |
9534 |
|
|
9535 |
if (vflag) { |
if (vflag) { |
9536 |
char save = *q; |
char save = *q; |
9537 |
*q = '\0'; |
*q = '\0'; |
9538 |
out2str(parsenextc); |
out2str(g_parsefile->next_to_pgetc); |
9539 |
*q = save; |
*q = save; |
9540 |
} |
} |
9541 |
|
|
9542 |
pgetc_debug("preadbuffer at %d:%p'%s'", parsenleft, parsenextc, parsenextc); |
pgetc_debug("preadbuffer at %d:%p'%s'", |
9543 |
return signed_char2int(*parsenextc++); |
g_parsefile->left_in_line, |
9544 |
|
g_parsefile->next_to_pgetc, |
9545 |
|
g_parsefile->next_to_pgetc); |
9546 |
|
return (unsigned char)*g_parsefile->next_to_pgetc++; |
9547 |
} |
} |
9548 |
|
|
9549 |
#define pgetc_as_macro() (--parsenleft >= 0 ? signed_char2int(*parsenextc++) : preadbuffer()) |
#define pgetc_as_macro() \ |
9550 |
|
(--g_parsefile->left_in_line >= 0 \ |
9551 |
|
? (unsigned char)*g_parsefile->next_to_pgetc++ \ |
9552 |
|
: preadbuffer() \ |
9553 |
|
) |
9554 |
|
|
9555 |
static int |
static int |
9556 |
pgetc(void) |
pgetc(void) |
9557 |
{ |
{ |
9558 |
pgetc_debug("pgetc at %d:%p'%s'", parsenleft, parsenextc, parsenextc); |
pgetc_debug("pgetc_fast at %d:%p'%s'", |
9559 |
|
g_parsefile->left_in_line, |
9560 |
|
g_parsefile->next_to_pgetc, |
9561 |
|
g_parsefile->next_to_pgetc); |
9562 |
return pgetc_as_macro(); |
return pgetc_as_macro(); |
9563 |
} |
} |
9564 |
|
|
9565 |
#if ENABLE_ASH_OPTIMIZE_FOR_SIZE |
#if ENABLE_ASH_OPTIMIZE_FOR_SIZE |
9566 |
#define pgetc_fast() pgetc() |
# define pgetc_fast() pgetc() |
9567 |
#else |
#else |
9568 |
#define pgetc_fast() pgetc_as_macro() |
# define pgetc_fast() pgetc_as_macro() |
9569 |
#endif |
#endif |
9570 |
|
|
|
/* |
|
|
* Same as pgetc(), but ignores PEOA. |
|
|
*/ |
|
9571 |
#if ENABLE_ASH_ALIAS |
#if ENABLE_ASH_ALIAS |
9572 |
static int |
static int |
9573 |
pgetc2(void) |
pgetc_without_PEOA(void) |
9574 |
{ |
{ |
9575 |
int c; |
int c; |
9576 |
do { |
do { |
9577 |
|
pgetc_debug("pgetc_fast at %d:%p'%s'", |
9578 |
|
g_parsefile->left_in_line, |
9579 |
|
g_parsefile->next_to_pgetc, |
9580 |
|
g_parsefile->next_to_pgetc); |
9581 |
c = pgetc_fast(); |
c = pgetc_fast(); |
9582 |
} while (c == PEOA); |
} while (c == PEOA); |
9583 |
return c; |
return c; |
9584 |
} |
} |
9585 |
#else |
#else |
9586 |
#define pgetc2() pgetc() |
# define pgetc_without_PEOA() pgetc() |
9587 |
#endif |
#endif |
9588 |
|
|
9589 |
/* |
/* |
9597 |
int c; |
int c; |
9598 |
|
|
9599 |
while (--nleft > 0) { |
while (--nleft > 0) { |
9600 |
c = pgetc2(); |
c = pgetc_without_PEOA(); |
9601 |
if (c == PEOF) { |
if (c == PEOF) { |
9602 |
if (p == line) |
if (p == line) |
9603 |
return NULL; |
return NULL; |
9618 |
static void |
static void |
9619 |
pungetc(void) |
pungetc(void) |
9620 |
{ |
{ |
9621 |
parsenleft++; |
g_parsefile->left_in_line++; |
9622 |
parsenextc--; |
g_parsefile->next_to_pgetc--; |
9623 |
pgetc_debug("pushed back to %d:%p'%s'", parsenleft, parsenextc, parsenextc); |
pgetc_debug("pushed back to %d:%p'%s'", |
9624 |
} |
g_parsefile->left_in_line, |
9625 |
|
g_parsefile->next_to_pgetc, |
9626 |
/* |
g_parsefile->next_to_pgetc); |
|
* Push a string back onto the input at this current parsefile level. |
|
|
* We handle aliases this way. |
|
|
*/ |
|
|
#if !ENABLE_ASH_ALIAS |
|
|
#define pushstring(s, ap) pushstring(s) |
|
|
#endif |
|
|
static void |
|
|
pushstring(char *s, struct alias *ap) |
|
|
{ |
|
|
struct strpush *sp; |
|
|
int len; |
|
|
|
|
|
len = strlen(s); |
|
|
INT_OFF; |
|
|
if (g_parsefile->strpush) { |
|
|
sp = ckzalloc(sizeof(*sp)); |
|
|
sp->prev = g_parsefile->strpush; |
|
|
} else { |
|
|
sp = &(g_parsefile->basestrpush); |
|
|
} |
|
|
g_parsefile->strpush = sp; |
|
|
sp->prevstring = parsenextc; |
|
|
sp->prevnleft = parsenleft; |
|
|
#if ENABLE_ASH_ALIAS |
|
|
sp->ap = ap; |
|
|
if (ap) { |
|
|
ap->flag |= ALIASINUSE; |
|
|
sp->string = s; |
|
|
} |
|
|
#endif |
|
|
parsenextc = s; |
|
|
parsenleft = len; |
|
|
INT_ON; |
|
9627 |
} |
} |
9628 |
|
|
9629 |
/* |
/* |
9635 |
{ |
{ |
9636 |
struct parsefile *pf; |
struct parsefile *pf; |
9637 |
|
|
|
g_parsefile->nleft = parsenleft; |
|
|
g_parsefile->lleft = parselleft; |
|
|
g_parsefile->nextc = parsenextc; |
|
|
g_parsefile->linno = plinno; |
|
9638 |
pf = ckzalloc(sizeof(*pf)); |
pf = ckzalloc(sizeof(*pf)); |
9639 |
pf->prev = g_parsefile; |
pf->prev = g_parsefile; |
9640 |
pf->fd = -1; |
pf->fd = -1; |
9656 |
popstring(); |
popstring(); |
9657 |
g_parsefile = pf->prev; |
g_parsefile = pf->prev; |
9658 |
free(pf); |
free(pf); |
|
parsenleft = g_parsefile->nleft; |
|
|
parselleft = g_parsefile->lleft; |
|
|
parsenextc = g_parsefile->nextc; |
|
|
plinno = g_parsefile->linno; |
|
9659 |
INT_ON; |
INT_ON; |
9660 |
} |
} |
9661 |
|
|
9698 |
g_parsefile->fd = fd; |
g_parsefile->fd = fd; |
9699 |
if (g_parsefile->buf == NULL) |
if (g_parsefile->buf == NULL) |
9700 |
g_parsefile->buf = ckmalloc(IBUFSIZ); |
g_parsefile->buf = ckmalloc(IBUFSIZ); |
9701 |
parselleft = parsenleft = 0; |
g_parsefile->left_in_buffer = 0; |
9702 |
plinno = 1; |
g_parsefile->left_in_line = 0; |
9703 |
|
g_parsefile->linno = 1; |
9704 |
} |
} |
9705 |
|
|
9706 |
/* |
/* |
9718 |
if (fd < 0) { |
if (fd < 0) { |
9719 |
if (flags & INPUT_NOFILE_OK) |
if (flags & INPUT_NOFILE_OK) |
9720 |
goto out; |
goto out; |
9721 |
ash_msg_and_raise_error("can't open %s", fname); |
ash_msg_and_raise_error("can't open '%s'", fname); |
9722 |
} |
} |
9723 |
if (fd < 10) { |
if (fd < 10) { |
9724 |
fd2 = copyfd(fd, 10); |
fd2 = copyfd(fd, 10); |
9741 |
{ |
{ |
9742 |
INT_OFF; |
INT_OFF; |
9743 |
pushfile(); |
pushfile(); |
9744 |
parsenextc = string; |
g_parsefile->next_to_pgetc = string; |
9745 |
parsenleft = strlen(string); |
g_parsefile->left_in_line = strlen(string); |
9746 |
g_parsefile->buf = NULL; |
g_parsefile->buf = NULL; |
9747 |
plinno = 1; |
g_parsefile->linno = 1; |
9748 |
INT_ON; |
INT_ON; |
9749 |
} |
} |
9750 |
|
|
9782 |
setstackmark(&smark); |
setstackmark(&smark); |
9783 |
mpath = mpathset() ? mpathval() : mailval(); |
mpath = mpathset() ? mpathval() : mailval(); |
9784 |
for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) { |
for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) { |
9785 |
p = padvance(&mpath, nullstr); |
p = path_advance(&mpath, nullstr); |
9786 |
if (p == NULL) |
if (p == NULL) |
9787 |
break; |
break; |
9788 |
if (*p == '\0') |
if (*p == '\0') |
9810 |
popstackmark(&smark); |
popstackmark(&smark); |
9811 |
} |
} |
9812 |
|
|
9813 |
static void |
static void FAST_FUNC |
9814 |
changemail(const char *val UNUSED_PARAM) |
changemail(const char *val UNUSED_PARAM) |
9815 |
{ |
{ |
9816 |
mail_var_path_changed = 1; |
mail_var_path_changed = 1; |
9966 |
/* |
/* |
9967 |
* The shift builtin command. |
* The shift builtin command. |
9968 |
*/ |
*/ |
9969 |
static int |
static int FAST_FUNC |
9970 |
shiftcmd(int argc UNUSED_PARAM, char **argv) |
shiftcmd(int argc UNUSED_PARAM, char **argv) |
9971 |
{ |
{ |
9972 |
int n; |
int n; |
10028 |
/* |
/* |
10029 |
* The set command builtin. |
* The set command builtin. |
10030 |
*/ |
*/ |
10031 |
static int |
static int FAST_FUNC |
10032 |
setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
10033 |
{ |
{ |
10034 |
int retval; |
int retval; |
10049 |
} |
} |
10050 |
|
|
10051 |
#if ENABLE_ASH_RANDOM_SUPPORT |
#if ENABLE_ASH_RANDOM_SUPPORT |
10052 |
static void |
static void FAST_FUNC |
10053 |
change_random(const char *value) |
change_random(const char *value) |
10054 |
{ |
{ |
10055 |
/* Galois LFSR parameter */ |
uint32_t t; |
|
/* Taps at 32 31 29 1: */ |
|
|
enum { MASK = 0x8000000b }; |
|
|
/* Another example - taps at 32 31 30 10: */ |
|
|
/* MASK = 0x00400007 */ |
|
10056 |
|
|
10057 |
if (value == NULL) { |
if (value == NULL) { |
10058 |
/* "get", generate */ |
/* "get", generate */ |
10059 |
uint32_t t; |
t = next_random(&random_gen); |
|
|
|
|
/* LCG has period of 2^32 and alternating lowest bit */ |
|
|
random_LCG = 1664525 * random_LCG + 1013904223; |
|
|
/* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */ |
|
|
t = (random_galois_LFSR << 1); |
|
|
if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */ |
|
|
t ^= MASK; |
|
|
random_galois_LFSR = t; |
|
|
/* Both are weak, combining them gives better randomness |
|
|
* and ~2^64 period. & 0x7fff is probably bash compat |
|
|
* for $RANDOM range. Combining with subtraction is |
|
|
* just for fun. + and ^ would work equally well. */ |
|
|
t = (t - random_LCG) & 0x7fff; |
|
10060 |
/* set without recursion */ |
/* set without recursion */ |
10061 |
setvar(vrandom.text, utoa(t), VNOFUNC); |
setvar(vrandom.text, utoa(t), VNOFUNC); |
10062 |
vrandom.flags &= ~VNOFUNC; |
vrandom.flags &= ~VNOFUNC; |
10063 |
} else { |
} else { |
10064 |
/* set/reset */ |
/* set/reset */ |
10065 |
random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10); |
t = strtoul(value, NULL, 10); |
10066 |
|
INIT_RANDOM_T(&random_gen, (t ? t : 1), t); |
10067 |
} |
} |
10068 |
} |
} |
10069 |
#endif |
#endif |
10163 |
* be processed in the current argument. If shellparam.optnext is NULL, |
* be processed in the current argument. If shellparam.optnext is NULL, |
10164 |
* then it's the first time getopts has been called. |
* then it's the first time getopts has been called. |
10165 |
*/ |
*/ |
10166 |
static int |
static int FAST_FUNC |
10167 |
getoptscmd(int argc, char **argv) |
getoptscmd(int argc, char **argv) |
10168 |
{ |
{ |
10169 |
char **optbase; |
char **optbase; |
10208 |
static struct nodelist *backquotelist; |
static struct nodelist *backquotelist; |
10209 |
static union node *redirnode; |
static union node *redirnode; |
10210 |
static struct heredoc *heredoc; |
static struct heredoc *heredoc; |
|
/* |
|
|
* NEOF is returned by parsecmd when it encounters an end of file. It |
|
|
* must be distinct from NULL, so we use the address of a variable that |
|
|
* happens to be handy. |
|
|
*/ |
|
|
#define NEOF ((union node *)&tokpushback) |
|
|
|
|
|
static void raise_error_syntax(const char *) NORETURN; |
|
|
static void |
|
|
raise_error_syntax(const char *msg) |
|
|
{ |
|
|
ash_msg_and_raise_error("syntax error: %s", msg); |
|
|
/* NOTREACHED */ |
|
|
} |
|
10211 |
|
|
10212 |
/* |
/* |
10213 |
* Called when an unexpected token is read during the parse. The argument |
* Called when an unexpected token is read during the parse. The argument |
10221 |
char msg[64]; |
char msg[64]; |
10222 |
int l; |
int l; |
10223 |
|
|
10224 |
l = sprintf(msg, "%s unexpected", tokname(lasttoken)); |
l = sprintf(msg, "unexpected %s", tokname(lasttoken)); |
10225 |
if (token >= 0) |
if (token >= 0) |
10226 |
sprintf(msg + l, " (expecting %s)", tokname(token)); |
sprintf(msg + l, " (expecting %s)", tokname(token)); |
10227 |
raise_error_syntax(msg); |
raise_error_syntax(msg); |
10413 |
* or backquotes). |
* or backquotes). |
10414 |
*/ |
*/ |
10415 |
static int |
static int |
10416 |
noexpand(char *text) |
noexpand(const char *text) |
10417 |
{ |
{ |
10418 |
char *p; |
unsigned char c; |
|
char c; |
|
10419 |
|
|
10420 |
p = text; |
while ((c = *text++) != '\0') { |
|
while ((c = *p++) != '\0') { |
|
10421 |
if (c == CTLQUOTEMARK) |
if (c == CTLQUOTEMARK) |
10422 |
continue; |
continue; |
10423 |
if (c == CTLESC) |
if (c == CTLESC) |
10424 |
p++; |
text++; |
10425 |
else if (SIT(c, BASESYNTAX) == CCTL) |
else if (SIT(c, BASESYNTAX) == CCTL) |
10426 |
return 0; |
return 0; |
10427 |
} |
} |
10445 |
TRACE(("Here document %d\n", n->type)); |
TRACE(("Here document %d\n", n->type)); |
10446 |
if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) |
if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) |
10447 |
raise_error_syntax("illegal eof marker for << redirection"); |
raise_error_syntax("illegal eof marker for << redirection"); |
10448 |
rmescapes(wordtext); |
rmescapes(wordtext, 0); |
10449 |
here->eofmark = wordtext; |
here->eofmark = wordtext; |
10450 |
here->next = NULL; |
here->next = NULL; |
10451 |
if (heredoclist == NULL) |
if (heredoclist == NULL) |
10807 |
* If eofmark is NULL, read a word or a redirection symbol. If eofmark |
* If eofmark is NULL, read a word or a redirection symbol. If eofmark |
10808 |
* is not NULL, read a here document. In the latter case, eofmark is the |
* is not NULL, read a here document. In the latter case, eofmark is the |
10809 |
* word which marks the end of the document and striptabs is true if |
* word which marks the end of the document and striptabs is true if |
10810 |
* leading tabs should be stripped from the document. The argument firstc |
* leading tabs should be stripped from the document. The argument c |
10811 |
* is the first character of the input token or document. |
* is the first character of the input token or document. |
10812 |
* |
* |
10813 |
* Because C does not have internal subroutines, I have simulated them |
* Because C does not have internal subroutines, I have simulated them |
10821 |
#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} |
#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} |
10822 |
#define PARSEARITH() {goto parsearith; parsearith_return:;} |
#define PARSEARITH() {goto parsearith; parsearith_return:;} |
10823 |
static int |
static int |
10824 |
readtoken1(int firstc, int syntax, char *eofmark, int striptabs) |
readtoken1(int c, int syntax, char *eofmark, int striptabs) |
10825 |
{ |
{ |
10826 |
/* NB: syntax parameter fits into smallint */ |
/* NB: syntax parameter fits into smallint */ |
10827 |
int c = firstc; |
/* c parameter is an unsigned char or PEOF or PEOA */ |
10828 |
char *out; |
char *out; |
10829 |
int len; |
int len; |
10830 |
char line[EOFMARKLEN + 1]; |
char line[EOFMARKLEN + 1]; |
10841 |
int parenlevel; /* levels of parens in arithmetic */ |
int parenlevel; /* levels of parens in arithmetic */ |
10842 |
int dqvarnest; /* levels of variables expansion within double quotes */ |
int dqvarnest; /* levels of variables expansion within double quotes */ |
10843 |
|
|
10844 |
USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;) |
IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;) |
10845 |
|
|
10846 |
#if __GNUC__ |
#if __GNUC__ |
10847 |
/* Avoid longjmp clobbering */ |
/* Avoid longjmp clobbering */ |
10856 |
(void) &prevsyntax; |
(void) &prevsyntax; |
10857 |
(void) &syntax; |
(void) &syntax; |
10858 |
#endif |
#endif |
10859 |
startlinno = plinno; |
startlinno = g_parsefile->linno; |
10860 |
bqlist = NULL; |
bqlist = NULL; |
10861 |
quotef = 0; |
quotef = 0; |
10862 |
oldstyle = 0; |
oldstyle = 0; |
10884 |
if (syntax == BASESYNTAX) |
if (syntax == BASESYNTAX) |
10885 |
goto endword; /* exit outer loop */ |
goto endword; /* exit outer loop */ |
10886 |
USTPUTC(c, out); |
USTPUTC(c, out); |
10887 |
plinno++; |
g_parsefile->linno++; |
10888 |
if (doprompt) |
if (doprompt) |
10889 |
setprompt(2); |
setprompt(2); |
10890 |
c = pgetc(); |
c = pgetc(); |
10907 |
USTPUTC(c, out); |
USTPUTC(c, out); |
10908 |
break; |
break; |
10909 |
case CBACK: /* backslash */ |
case CBACK: /* backslash */ |
10910 |
c = pgetc2(); |
c = pgetc_without_PEOA(); |
10911 |
if (c == PEOF) { |
if (c == PEOF) { |
10912 |
USTPUTC(CTLESC, out); |
USTPUTC(CTLESC, out); |
10913 |
USTPUTC('\\', out); |
USTPUTC('\\', out); |
10947 |
dblquote = 1; |
dblquote = 1; |
10948 |
goto quotemark; |
goto quotemark; |
10949 |
case CENDQUOTE: |
case CENDQUOTE: |
10950 |
USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;) |
IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;) |
10951 |
if (eofmark != NULL && arinest == 0 |
if (eofmark != NULL && arinest == 0 |
10952 |
&& varnest == 0 |
&& varnest == 0 |
10953 |
) { |
) { |
10975 |
USTPUTC(c, out); |
USTPUTC(c, out); |
10976 |
} |
} |
10977 |
break; |
break; |
10978 |
#if ENABLE_ASH_MATH_SUPPORT |
#if ENABLE_SH_MATH_SUPPORT |
10979 |
case CLP: /* '(' in arithmetic */ |
case CLP: /* '(' in arithmetic */ |
10980 |
parenlevel++; |
parenlevel++; |
10981 |
USTPUTC(c, out); |
USTPUTC(c, out); |
11021 |
#endif |
#endif |
11022 |
goto endword; /* exit outer loop */ |
goto endword; /* exit outer loop */ |
11023 |
} |
} |
11024 |
#if ENABLE_ASH_ALIAS |
IF_ASH_ALIAS(if (c != PEOA)) |
|
if (c != PEOA) |
|
|
#endif |
|
11025 |
USTPUTC(c, out); |
USTPUTC(c, out); |
11026 |
|
|
11027 |
} |
} |
11029 |
} /* for (;;) */ |
} /* for (;;) */ |
11030 |
} |
} |
11031 |
endword: |
endword: |
11032 |
#if ENABLE_ASH_MATH_SUPPORT |
#if ENABLE_SH_MATH_SUPPORT |
11033 |
if (syntax == ARISYNTAX) |
if (syntax == ARISYNTAX) |
11034 |
raise_error_syntax("missing '))'"); |
raise_error_syntax("missing '))'"); |
11035 |
#endif |
#endif |
11036 |
if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL) |
if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL) |
11037 |
raise_error_syntax("unterminated quoted string"); |
raise_error_syntax("unterminated quoted string"); |
11038 |
if (varnest != 0) { |
if (varnest != 0) { |
11039 |
startlinno = plinno; |
startlinno = g_parsefile->linno; |
11040 |
/* { */ |
/* { */ |
11041 |
raise_error_syntax("missing '}'"); |
raise_error_syntax("missing '}'"); |
11042 |
} |
} |
11044 |
len = out - (char *)stackblock(); |
len = out - (char *)stackblock(); |
11045 |
out = stackblock(); |
out = stackblock(); |
11046 |
if (eofmark == NULL) { |
if (eofmark == NULL) { |
11047 |
if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>')) |
if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>')) |
11048 |
&& quotef == 0 |
&& quotef == 0 |
11049 |
) { |
) { |
11050 |
if (isdigit_str9(out)) { |
if (isdigit_str9(out)) { |
11073 |
checkend: { |
checkend: { |
11074 |
if (eofmark) { |
if (eofmark) { |
11075 |
#if ENABLE_ASH_ALIAS |
#if ENABLE_ASH_ALIAS |
11076 |
if (c == PEOA) { |
if (c == PEOA) |
11077 |
c = pgetc2(); |
c = pgetc_without_PEOA(); |
|
} |
|
11078 |
#endif |
#endif |
11079 |
if (striptabs) { |
if (striptabs) { |
11080 |
while (c == '\t') { |
while (c == '\t') { |
11081 |
c = pgetc2(); |
c = pgetc_without_PEOA(); |
11082 |
} |
} |
11083 |
} |
} |
11084 |
if (c == *eofmark) { |
if (c == *eofmark) { |
11090 |
continue; |
continue; |
11091 |
if (*p == '\n' && *q == '\0') { |
if (*p == '\n' && *q == '\0') { |
11092 |
c = PEOF; |
c = PEOF; |
11093 |
plinno++; |
g_parsefile->linno++; |
11094 |
needprompt = doprompt; |
needprompt = doprompt; |
11095 |
} else { |
} else { |
11096 |
pushstring(line, NULL); |
pushstring(line, NULL); |
11186 |
(((unsigned)(c) - 33 < 32) \ |
(((unsigned)(c) - 33 < 32) \ |
11187 |
&& ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1)) |
&& ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1)) |
11188 |
parsesub: { |
parsesub: { |
11189 |
int subtype; |
unsigned char subtype; |
11190 |
int typeloc; |
int typeloc; |
11191 |
int flags; |
int flags; |
11192 |
char *p; |
char *p; |
11193 |
static const char types[] ALIGN1 = "}-+?="; |
static const char types[] ALIGN1 = "}-+?="; |
11194 |
|
|
11195 |
c = pgetc(); |
c = pgetc(); |
11196 |
if (c <= PEOA_OR_PEOF |
if (c > 255 /* PEOA or PEOF */ |
11197 |
|| (c != '(' && c != '{' && !is_name(c) && !is_special(c)) |
|| (c != '(' && c != '{' && !is_name(c) && !is_special(c)) |
11198 |
) { |
) { |
11199 |
#if ENABLE_ASH_BASH_COMPAT |
#if ENABLE_ASH_BASH_COMPAT |
11205 |
pungetc(); |
pungetc(); |
11206 |
} else if (c == '(') { /* $(command) or $((arith)) */ |
} else if (c == '(') { /* $(command) or $((arith)) */ |
11207 |
if (pgetc() == '(') { |
if (pgetc() == '(') { |
11208 |
#if ENABLE_ASH_MATH_SUPPORT |
#if ENABLE_SH_MATH_SUPPORT |
11209 |
PARSEARITH(); |
PARSEARITH(); |
11210 |
#else |
#else |
11211 |
raise_error_syntax("you disabled math support for $((arith)) syntax"); |
raise_error_syntax("you disabled math support for $((arith)) syntax"); |
11230 |
} else |
} else |
11231 |
subtype = 0; |
subtype = 0; |
11232 |
} |
} |
11233 |
if (c > PEOA_OR_PEOF && is_name(c)) { |
if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) { |
11234 |
do { |
do { |
11235 |
STPUTC(c, out); |
STPUTC(c, out); |
11236 |
c = pgetc(); |
c = pgetc(); |
11237 |
} while (c > PEOA_OR_PEOF && is_in_name(c)); |
} while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c)); |
11238 |
} else if (isdigit(c)) { |
} else if (isdigit(c)) { |
11239 |
do { |
do { |
11240 |
STPUTC(c, out); |
STPUTC(c, out); |
11247 |
badsub: |
badsub: |
11248 |
raise_error_syntax("bad substitution"); |
raise_error_syntax("bad substitution"); |
11249 |
} |
} |
11250 |
|
if (c != '}' && subtype == VSLENGTH) |
11251 |
|
goto badsub; |
11252 |
|
|
11253 |
STPUTC('=', out); |
STPUTC('=', out); |
11254 |
flags = 0; |
flags = 0; |
11298 |
} |
} |
11299 |
if (dblquote || arinest) |
if (dblquote || arinest) |
11300 |
flags |= VSQUOTE; |
flags |= VSQUOTE; |
11301 |
*((char *)stackblock() + typeloc) = subtype | flags; |
((unsigned char *)stackblock())[typeloc] = subtype | flags; |
11302 |
if (subtype != VSNORMAL) { |
if (subtype != VSNORMAL) { |
11303 |
varnest++; |
varnest++; |
11304 |
if (dblquote || arinest) { |
if (dblquote || arinest) { |
11368 |
case '\\': |
case '\\': |
11369 |
pc = pgetc(); |
pc = pgetc(); |
11370 |
if (pc == '\n') { |
if (pc == '\n') { |
11371 |
plinno++; |
g_parsefile->linno++; |
11372 |
if (doprompt) |
if (doprompt) |
11373 |
setprompt(2); |
setprompt(2); |
11374 |
/* |
/* |
11380 |
continue; |
continue; |
11381 |
} |
} |
11382 |
if (pc != '\\' && pc != '`' && pc != '$' |
if (pc != '\\' && pc != '`' && pc != '$' |
11383 |
&& (!dblquote || pc != '"')) |
&& (!dblquote || pc != '"') |
11384 |
|
) { |
11385 |
STPUTC('\\', pout); |
STPUTC('\\', pout); |
11386 |
if (pc > PEOA_OR_PEOF) { |
} |
11387 |
|
if (pc <= 255 /* not PEOA or PEOF */) { |
11388 |
break; |
break; |
11389 |
} |
} |
11390 |
/* fall through */ |
/* fall through */ |
11391 |
|
|
11392 |
case PEOF: |
case PEOF: |
11393 |
#if ENABLE_ASH_ALIAS |
IF_ASH_ALIAS(case PEOA:) |
11394 |
case PEOA: |
startlinno = g_parsefile->linno; |
|
#endif |
|
|
startlinno = plinno; |
|
11395 |
raise_error_syntax("EOF in backquote substitution"); |
raise_error_syntax("EOF in backquote substitution"); |
11396 |
|
|
11397 |
case '\n': |
case '\n': |
11398 |
plinno++; |
g_parsefile->linno++; |
11399 |
needprompt = doprompt; |
needprompt = doprompt; |
11400 |
break; |
break; |
11401 |
|
|
11462 |
goto parsebackq_newreturn; |
goto parsebackq_newreturn; |
11463 |
} |
} |
11464 |
|
|
11465 |
#if ENABLE_ASH_MATH_SUPPORT |
#if ENABLE_SH_MATH_SUPPORT |
11466 |
/* |
/* |
11467 |
* Parse an arithmetic expansion (indicate start of one and set state) |
* Parse an arithmetic expansion (indicate start of one and set state) |
11468 |
*/ |
*/ |
11536 |
if (needprompt) { |
if (needprompt) { |
11537 |
setprompt(2); |
setprompt(2); |
11538 |
} |
} |
11539 |
startlinno = plinno; |
startlinno = g_parsefile->linno; |
11540 |
for (;;) { /* until token or start of word found */ |
for (;;) { /* until token or start of word found */ |
11541 |
c = pgetc_fast(); |
c = pgetc_fast(); |
11542 |
if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA)) |
if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA)) |
11543 |
continue; |
continue; |
11544 |
|
|
11545 |
if (c == '#') { |
if (c == '#') { |
11551 |
pungetc(); |
pungetc(); |
11552 |
break; /* return readtoken1(...) */ |
break; /* return readtoken1(...) */ |
11553 |
} |
} |
11554 |
startlinno = ++plinno; |
startlinno = ++g_parsefile->linno; |
11555 |
if (doprompt) |
if (doprompt) |
11556 |
setprompt(2); |
setprompt(2); |
11557 |
} else { |
} else { |
11560 |
p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1; |
p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1; |
11561 |
if (c != PEOF) { |
if (c != PEOF) { |
11562 |
if (c == '\n') { |
if (c == '\n') { |
11563 |
plinno++; |
g_parsefile->linno++; |
11564 |
needprompt = doprompt; |
needprompt = doprompt; |
11565 |
} |
} |
11566 |
|
|
11602 |
if (needprompt) { |
if (needprompt) { |
11603 |
setprompt(2); |
setprompt(2); |
11604 |
} |
} |
11605 |
startlinno = plinno; |
startlinno = g_parsefile->linno; |
11606 |
for (;;) { /* until token or start of word found */ |
for (;;) { /* until token or start of word found */ |
11607 |
c = pgetc_fast(); |
c = pgetc_fast(); |
11608 |
switch (c) { |
switch (c) { |
11609 |
case ' ': case '\t': |
case ' ': case '\t': |
11610 |
#if ENABLE_ASH_ALIAS |
IF_ASH_ALIAS(case PEOA:) |
|
case PEOA: |
|
|
#endif |
|
11611 |
continue; |
continue; |
11612 |
case '#': |
case '#': |
11613 |
while ((c = pgetc()) != '\n' && c != PEOF) |
while ((c = pgetc()) != '\n' && c != PEOF) |
11616 |
continue; |
continue; |
11617 |
case '\\': |
case '\\': |
11618 |
if (pgetc() == '\n') { |
if (pgetc() == '\n') { |
11619 |
startlinno = ++plinno; |
startlinno = ++g_parsefile->linno; |
11620 |
if (doprompt) |
if (doprompt) |
11621 |
setprompt(2); |
setprompt(2); |
11622 |
continue; |
continue; |
11624 |
pungetc(); |
pungetc(); |
11625 |
goto breakloop; |
goto breakloop; |
11626 |
case '\n': |
case '\n': |
11627 |
plinno++; |
g_parsefile->linno++; |
11628 |
needprompt = doprompt; |
needprompt = doprompt; |
11629 |
RETURN(TNL); |
RETURN(TNL); |
11630 |
case PEOF: |
case PEOF: |
11734 |
} |
} |
11735 |
|
|
11736 |
/* |
/* |
11737 |
* Read and parse a command. Returns NEOF on end of file. (NULL is a |
* Read and parse a command. Returns NODE_EOF on end of file. |
11738 |
* valid parse tree indicating a blank line.) |
* (NULL is a valid parse tree indicating a blank line.) |
11739 |
*/ |
*/ |
11740 |
static union node * |
static union node * |
11741 |
parsecmd(int interact) |
parsecmd(int interact) |
11749 |
needprompt = 0; |
needprompt = 0; |
11750 |
t = readtoken(); |
t = readtoken(); |
11751 |
if (t == TEOF) |
if (t == TEOF) |
11752 |
return NEOF; |
return NODE_EOF; |
11753 |
if (t == TNL) |
if (t == TNL) |
11754 |
return NULL; |
return NULL; |
11755 |
tokpushback = 1; |
tokpushback = 1; |
11794 |
{ |
{ |
11795 |
union node n; |
union node n; |
11796 |
|
|
11797 |
/* XXX Fix (char *) cast. */ |
/* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value, |
11798 |
|
* and token processing _can_ alter it (delete NULs etc). */ |
11799 |
setinputstring((char *)ps); |
setinputstring((char *)ps); |
11800 |
readtoken1(pgetc(), PSSYNTAX, nullstr, 0); |
readtoken1(pgetc(), PSSYNTAX, nullstr, 0); |
11801 |
popfile(); |
popfile(); |
11824 |
setstackmark(&smark); |
setstackmark(&smark); |
11825 |
|
|
11826 |
skip = 0; |
skip = 0; |
11827 |
while ((n = parsecmd(0)) != NEOF) { |
while ((n = parsecmd(0)) != NODE_EOF) { |
11828 |
evaltree(n, 0); |
evaltree(n, 0); |
11829 |
popstackmark(&smark); |
popstackmark(&smark); |
11830 |
skip = evalskip; |
skip = evalskip; |
11841 |
/* |
/* |
11842 |
* The eval command. |
* The eval command. |
11843 |
*/ |
*/ |
11844 |
static int |
static int FAST_FUNC |
11845 |
evalcmd(int argc UNUSED_PARAM, char **argv) |
evalcmd(int argc UNUSED_PARAM, char **argv) |
11846 |
{ |
{ |
11847 |
char *p; |
char *p; |
11869 |
} |
} |
11870 |
|
|
11871 |
/* |
/* |
11872 |
* Read and execute commands. "Top" is nonzero for the top level command |
* Read and execute commands. |
11873 |
* loop; it turns on prompting if the shell is interactive. |
* "Top" is nonzero for the top level command loop; |
11874 |
|
* it turns on prompting if the shell is interactive. |
11875 |
*/ |
*/ |
11876 |
static int |
static int |
11877 |
cmdloop(int top) |
cmdloop(int top) |
11898 |
#endif |
#endif |
11899 |
} |
} |
11900 |
n = parsecmd(inter); |
n = parsecmd(inter); |
11901 |
/* showtree(n); DEBUG */ |
#if DEBUG |
11902 |
if (n == NEOF) { |
if (DEBUG > 2 && debug && (n != NODE_EOF)) |
11903 |
|
showtree(n); |
11904 |
|
#endif |
11905 |
|
if (n == NODE_EOF) { |
11906 |
if (!top || numeof >= 50) |
if (!top || numeof >= 50) |
11907 |
break; |
break; |
11908 |
if (!stoppedjobs()) { |
if (!stoppedjobs()) { |
11943 |
if (strchr(name, '/')) |
if (strchr(name, '/')) |
11944 |
return name; |
return name; |
11945 |
|
|
11946 |
while ((fullname = padvance(&path, name)) != NULL) { |
/* IIRC standards do not say whether . is to be searched. |
11947 |
|
* And it is even smaller this way, making it unconditional for now: |
11948 |
|
*/ |
11949 |
|
if (1) { /* ENABLE_ASH_BASH_COMPAT */ |
11950 |
|
fullname = name; |
11951 |
|
goto try_cur_dir; |
11952 |
|
} |
11953 |
|
|
11954 |
|
while ((fullname = path_advance(&path, name)) != NULL) { |
11955 |
|
try_cur_dir: |
11956 |
if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { |
if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { |
11957 |
/* |
/* |
11958 |
* Don't bother freeing here, since it will |
* Don't bother freeing here, since it will |
11960 |
*/ |
*/ |
11961 |
return fullname; |
return fullname; |
11962 |
} |
} |
11963 |
stunalloc(fullname); |
if (fullname != name) |
11964 |
|
stunalloc(fullname); |
11965 |
} |
} |
11966 |
|
|
11967 |
/* not found in the PATH */ |
/* not found in the PATH */ |
11969 |
/* NOTREACHED */ |
/* NOTREACHED */ |
11970 |
} |
} |
11971 |
|
|
11972 |
static int |
static int FAST_FUNC |
11973 |
dotcmd(int argc, char **argv) |
dotcmd(int argc, char **argv) |
11974 |
{ |
{ |
11975 |
struct strlist *sp; |
struct strlist *sp; |
12004 |
return status; |
return status; |
12005 |
} |
} |
12006 |
|
|
12007 |
static int |
static int FAST_FUNC |
12008 |
exitcmd(int argc UNUSED_PARAM, char **argv) |
exitcmd(int argc UNUSED_PARAM, char **argv) |
12009 |
{ |
{ |
12010 |
if (stoppedjobs()) |
if (stoppedjobs()) |
12135 |
e = ENOENT; |
e = ENOENT; |
12136 |
idx = -1; |
idx = -1; |
12137 |
loop: |
loop: |
12138 |
while ((fullname = padvance(&path, name)) != NULL) { |
while ((fullname = path_advance(&path, name)) != NULL) { |
12139 |
stunalloc(fullname); |
stunalloc(fullname); |
12140 |
/* NB: code below will still use fullname |
/* NB: code below will still use fullname |
12141 |
* despite it being "unallocated" */ |
* despite it being "unallocated" */ |
12228 |
/* |
/* |
12229 |
* The trap builtin. |
* The trap builtin. |
12230 |
*/ |
*/ |
12231 |
static int |
static int FAST_FUNC |
12232 |
trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
12233 |
{ |
{ |
12234 |
char *action; |
char *action; |
12239 |
ap = argptr; |
ap = argptr; |
12240 |
if (!*ap) { |
if (!*ap) { |
12241 |
for (signo = 0; signo < NSIG; signo++) { |
for (signo = 0; signo < NSIG; signo++) { |
12242 |
if (trap[signo] != NULL) { |
char *tr = trap_ptr[signo]; |
12243 |
|
if (tr) { |
12244 |
|
/* note: bash adds "SIG", but only if invoked |
12245 |
|
* as "bash". If called as "sh", or if set -o posix, |
12246 |
|
* then it prints short signal names. |
12247 |
|
* We are printing short names: */ |
12248 |
out1fmt("trap -- %s %s\n", |
out1fmt("trap -- %s %s\n", |
12249 |
single_quote(trap[signo]), |
single_quote(tr), |
12250 |
get_signame(signo)); |
get_signame(signo)); |
12251 |
|
/* trap_ptr != trap only if we are in special-cased `trap` code. |
12252 |
|
* In this case, we will exit very soon, no need to free(). */ |
12253 |
|
/* if (trap_ptr != trap && tp[0]) */ |
12254 |
|
/* free(tr); */ |
12255 |
} |
} |
12256 |
} |
} |
12257 |
|
/* |
12258 |
|
if (trap_ptr != trap) { |
12259 |
|
free(trap_ptr); |
12260 |
|
trap_ptr = trap; |
12261 |
|
} |
12262 |
|
*/ |
12263 |
return 0; |
return 0; |
12264 |
} |
} |
12265 |
|
|
12266 |
action = NULL; |
action = NULL; |
12267 |
if (ap[1]) |
if (ap[1]) |
12268 |
action = *ap++; |
action = *ap++; |
12294 |
/* |
/* |
12295 |
* Lists available builtins |
* Lists available builtins |
12296 |
*/ |
*/ |
12297 |
static int |
static int FAST_FUNC |
12298 |
helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
12299 |
{ |
{ |
12300 |
unsigned col; |
unsigned col; |
12301 |
unsigned i; |
unsigned i; |
12302 |
|
|
12303 |
out1fmt("\nBuilt-in commands:\n-------------------\n"); |
out1fmt( |
12304 |
|
"Built-in commands:\n" |
12305 |
|
"------------------\n"); |
12306 |
for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) { |
for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) { |
12307 |
col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), |
col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), |
12308 |
builtintab[i].name + 1); |
builtintab[i].name + 1); |
12332 |
/* |
/* |
12333 |
* The export and readonly commands. |
* The export and readonly commands. |
12334 |
*/ |
*/ |
12335 |
static int |
static int FAST_FUNC |
12336 |
exportcmd(int argc UNUSED_PARAM, char **argv) |
exportcmd(int argc UNUSED_PARAM, char **argv) |
12337 |
{ |
{ |
12338 |
struct var *vp; |
struct var *vp; |
12383 |
* variable to allow a function to be unset when there is a readonly variable |
* variable to allow a function to be unset when there is a readonly variable |
12384 |
* with the same name. |
* with the same name. |
12385 |
*/ |
*/ |
12386 |
static int |
static int FAST_FUNC |
12387 |
unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
12388 |
{ |
{ |
12389 |
char **ap; |
char **ap; |
12421 |
0 |
0 |
12422 |
}; |
}; |
12423 |
|
|
12424 |
static int |
static int FAST_FUNC |
12425 |
timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
12426 |
{ |
{ |
12427 |
long clk_tck, s, t; |
long clk_tck, s, t; |
12444 |
return 0; |
return 0; |
12445 |
} |
} |
12446 |
|
|
12447 |
#if ENABLE_ASH_MATH_SUPPORT |
#if ENABLE_SH_MATH_SUPPORT |
|
static arith_t |
|
|
dash_arith(const char *s) |
|
|
{ |
|
|
arith_t result; |
|
|
int errcode = 0; |
|
|
|
|
|
INT_OFF; |
|
|
result = arith(s, &errcode); |
|
|
if (errcode < 0) { |
|
|
if (errcode == -3) |
|
|
ash_msg_and_raise_error("exponent less than 0"); |
|
|
if (errcode == -2) |
|
|
ash_msg_and_raise_error("divide by zero"); |
|
|
if (errcode == -5) |
|
|
ash_msg_and_raise_error("expression recursion loop detected"); |
|
|
raise_error_syntax(s); |
|
|
} |
|
|
INT_ON; |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
12448 |
/* |
/* |
12449 |
* The let builtin. partial stolen from GNU Bash, the Bourne Again SHell. |
* The let builtin. partial stolen from GNU Bash, the Bourne Again SHell. |
12450 |
* Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc. |
* Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc. |
12451 |
* |
* |
12452 |
* Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru> |
* Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru> |
12453 |
*/ |
*/ |
12454 |
static int |
static int FAST_FUNC |
12455 |
letcmd(int argc UNUSED_PARAM, char **argv) |
letcmd(int argc UNUSED_PARAM, char **argv) |
12456 |
{ |
{ |
12457 |
arith_t i; |
arith_t i; |
12460 |
if (!*argv) |
if (!*argv) |
12461 |
ash_msg_and_raise_error("expression expected"); |
ash_msg_and_raise_error("expression expected"); |
12462 |
do { |
do { |
12463 |
i = dash_arith(*argv); |
i = ash_arith(*argv); |
12464 |
} while (*++argv); |
} while (*++argv); |
12465 |
|
|
12466 |
return !i; |
return !i; |
12467 |
} |
} |
12468 |
#endif /* ASH_MATH_SUPPORT */ |
#endif /* SH_MATH_SUPPORT */ |
12469 |
|
|
12470 |
|
|
12471 |
/* ============ miscbltin.c |
/* ============ miscbltin.c |
12493 |
* -d DELIM End on DELIM char, not newline |
* -d DELIM End on DELIM char, not newline |
12494 |
* -e Use line editing (tty only) |
* -e Use line editing (tty only) |
12495 |
*/ |
*/ |
12496 |
static int |
static int FAST_FUNC |
12497 |
readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
12498 |
{ |
{ |
12499 |
static const char *const arg_REPLY[] = { "REPLY", NULL }; |
char *opt_n = NULL; |
12500 |
|
char *opt_p = NULL; |
12501 |
char **ap; |
char *opt_t = NULL; |
12502 |
int backslash; |
char *opt_u = NULL; |
12503 |
char c; |
int read_flags = 0; |
12504 |
int rflag; |
const char *r; |
|
char *prompt; |
|
|
const char *ifs; |
|
|
char *p; |
|
|
int startword; |
|
|
int status; |
|
12505 |
int i; |
int i; |
12506 |
int fd = 0; |
|
12507 |
#if ENABLE_ASH_READ_NCHARS |
while ((i = nextopt("p:u:rt:n:s")) != '\0') { |
|
int nchars = 0; /* if != 0, -n is in effect */ |
|
|
int silent = 0; |
|
|
struct termios tty, old_tty; |
|
|
#endif |
|
|
#if ENABLE_ASH_READ_TIMEOUT |
|
|
unsigned end_ms = 0; |
|
|
unsigned timeout = 0; |
|
|
#endif |
|
|
|
|
|
rflag = 0; |
|
|
prompt = NULL; |
|
|
while ((i = nextopt("p:u:r" |
|
|
USE_ASH_READ_TIMEOUT("t:") |
|
|
USE_ASH_READ_NCHARS("n:s") |
|
|
)) != '\0') { |
|
12508 |
switch (i) { |
switch (i) { |
12509 |
case 'p': |
case 'p': |
12510 |
prompt = optionarg; |
opt_p = optionarg; |
12511 |
break; |
break; |
|
#if ENABLE_ASH_READ_NCHARS |
|
12512 |
case 'n': |
case 'n': |
12513 |
nchars = bb_strtou(optionarg, NULL, 10); |
opt_n = optionarg; |
|
if (nchars < 0 || errno) |
|
|
ash_msg_and_raise_error("invalid count"); |
|
|
/* nchars == 0: off (bash 3.2 does this too) */ |
|
12514 |
break; |
break; |
12515 |
case 's': |
case 's': |
12516 |
silent = 1; |
read_flags |= BUILTIN_READ_SILENT; |
12517 |
break; |
break; |
|
#endif |
|
|
#if ENABLE_ASH_READ_TIMEOUT |
|
12518 |
case 't': |
case 't': |
12519 |
timeout = bb_strtou(optionarg, NULL, 10); |
opt_t = optionarg; |
|
if (errno || timeout > UINT_MAX / 2048) |
|
|
ash_msg_and_raise_error("invalid timeout"); |
|
|
timeout *= 1000; |
|
|
#if 0 /* even bash have no -t N.NNN support */ |
|
|
ts.tv_sec = bb_strtou(optionarg, &p, 10); |
|
|
ts.tv_usec = 0; |
|
|
/* EINVAL means number is ok, but not terminated by NUL */ |
|
|
if (*p == '.' && errno == EINVAL) { |
|
|
char *p2; |
|
|
if (*++p) { |
|
|
int scale; |
|
|
ts.tv_usec = bb_strtou(p, &p2, 10); |
|
|
if (errno) |
|
|
ash_msg_and_raise_error("invalid timeout"); |
|
|
scale = p2 - p; |
|
|
/* normalize to usec */ |
|
|
if (scale > 6) |
|
|
ash_msg_and_raise_error("invalid timeout"); |
|
|
while (scale++ < 6) |
|
|
ts.tv_usec *= 10; |
|
|
} |
|
|
} else if (ts.tv_sec < 0 || errno) { |
|
|
ash_msg_and_raise_error("invalid timeout"); |
|
|
} |
|
|
if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */ |
|
|
ash_msg_and_raise_error("invalid timeout"); |
|
|
} |
|
|
#endif /* if 0 */ |
|
12520 |
break; |
break; |
|
#endif |
|
12521 |
case 'r': |
case 'r': |
12522 |
rflag = 1; |
read_flags |= BUILTIN_READ_RAW; |
12523 |
break; |
break; |
12524 |
case 'u': |
case 'u': |
12525 |
fd = bb_strtou(optionarg, NULL, 10); |
opt_u = optionarg; |
|
if (fd < 0 || errno) |
|
|
ash_msg_and_raise_error("invalid file descriptor"); |
|
12526 |
break; |
break; |
12527 |
default: |
default: |
12528 |
break; |
break; |
12529 |
} |
} |
12530 |
} |
} |
|
if (prompt && isatty(fd)) { |
|
|
out2str(prompt); |
|
|
} |
|
|
ap = argptr; |
|
|
if (*ap == NULL) |
|
|
ap = (char**)arg_REPLY; |
|
|
ifs = bltinlookup("IFS"); |
|
|
if (ifs == NULL) |
|
|
ifs = defifs; |
|
|
#if ENABLE_ASH_READ_NCHARS |
|
|
tcgetattr(fd, &tty); |
|
|
old_tty = tty; |
|
|
if (nchars || silent) { |
|
|
if (nchars) { |
|
|
tty.c_lflag &= ~ICANON; |
|
|
tty.c_cc[VMIN] = nchars < 256 ? nchars : 255; |
|
|
} |
|
|
if (silent) { |
|
|
tty.c_lflag &= ~(ECHO | ECHOK | ECHONL); |
|
|
} |
|
|
/* if tcgetattr failed, tcsetattr will fail too. |
|
|
* Ignoring, it's harmless. */ |
|
|
tcsetattr(fd, TCSANOW, &tty); |
|
|
} |
|
|
#endif |
|
12531 |
|
|
12532 |
status = 0; |
r = shell_builtin_read(setvar2, |
12533 |
startword = 2; |
argptr, |
12534 |
backslash = 0; |
bltinlookup("IFS"), /* can be NULL */ |
12535 |
#if ENABLE_ASH_READ_TIMEOUT |
read_flags, |
12536 |
if (timeout) /* NB: ensuring end_ms is nonzero */ |
opt_n, |
12537 |
end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1; |
opt_p, |
12538 |
#endif |
opt_t, |
12539 |
STARTSTACKSTR(p); |
opt_u |
12540 |
do { |
); |
|
const char *is_ifs; |
|
|
|
|
|
#if ENABLE_ASH_READ_TIMEOUT |
|
|
if (end_ms) { |
|
|
struct pollfd pfd[1]; |
|
|
pfd[0].fd = fd; |
|
|
pfd[0].events = POLLIN; |
|
|
timeout = end_ms - (unsigned)(monotonic_us() / 1000); |
|
|
if ((int)timeout <= 0 /* already late? */ |
|
|
|| safe_poll(pfd, 1, timeout) != 1 /* no? wait... */ |
|
|
) { /* timed out! */ |
|
|
#if ENABLE_ASH_READ_NCHARS |
|
|
tcsetattr(fd, TCSANOW, &old_tty); |
|
|
#endif |
|
|
return 1; |
|
|
} |
|
|
} |
|
|
#endif |
|
|
if (nonblock_safe_read(fd, &c, 1) != 1) { |
|
|
status = 1; |
|
|
break; |
|
|
} |
|
|
if (c == '\0') |
|
|
continue; |
|
|
if (backslash) { |
|
|
backslash = 0; |
|
|
if (c != '\n') |
|
|
goto put; |
|
|
continue; |
|
|
} |
|
|
if (!rflag && c == '\\') { |
|
|
backslash = 1; |
|
|
continue; |
|
|
} |
|
|
if (c == '\n') |
|
|
break; |
|
|
is_ifs = strchr(ifs, c); |
|
|
if (startword && is_ifs) { |
|
|
if (isspace(c)) |
|
|
continue; |
|
|
/* non-space ifs char */ |
|
|
startword--; |
|
|
if (startword == 1) /* first one? */ |
|
|
continue; |
|
|
} |
|
|
startword = 0; |
|
|
if (ap[1] != NULL && is_ifs) { |
|
|
const char *beg; |
|
|
STACKSTRNUL(p); |
|
|
beg = stackblock(); |
|
|
setvar(*ap, beg, 0); |
|
|
ap++; |
|
|
/* can we skip one non-space ifs? (2: yes) */ |
|
|
startword = isspace(c) ? 2 : 1; |
|
|
STARTSTACKSTR(p); |
|
|
continue; |
|
|
} |
|
|
put: |
|
|
STPUTC(c, p); |
|
|
} |
|
|
/* end of do {} while: */ |
|
|
#if ENABLE_ASH_READ_NCHARS |
|
|
while (--nchars); |
|
|
#else |
|
|
while (1); |
|
|
#endif |
|
12541 |
|
|
12542 |
#if ENABLE_ASH_READ_NCHARS |
if ((uintptr_t)r > 1) |
12543 |
tcsetattr(fd, TCSANOW, &old_tty); |
ash_msg_and_raise_error(r); |
|
#endif |
|
12544 |
|
|
12545 |
STACKSTRNUL(p); |
return (uintptr_t)r; |
|
/* Remove trailing space ifs chars */ |
|
|
while ((char *)stackblock() <= --p && isspace(*p) && strchr(ifs, *p) != NULL) |
|
|
*p = '\0'; |
|
|
setvar(*ap, stackblock(), 0); |
|
|
while (*++ap != NULL) |
|
|
setvar(*ap, nullstr, 0); |
|
|
return status; |
|
12546 |
} |
} |
12547 |
|
|
12548 |
static int |
static int FAST_FUNC |
12549 |
umaskcmd(int argc UNUSED_PARAM, char **argv) |
umaskcmd(int argc UNUSED_PARAM, char **argv) |
12550 |
{ |
{ |
12551 |
static const char permuser[3] ALIGN1 = "ugo"; |
static const char permuser[3] ALIGN1 = "ugo"; |
12556 |
S_IROTH, S_IWOTH, S_IXOTH |
S_IROTH, S_IWOTH, S_IXOTH |
12557 |
}; |
}; |
12558 |
|
|
12559 |
|
/* TODO: use bb_parse_mode() instead */ |
12560 |
|
|
12561 |
char *ap; |
char *ap; |
12562 |
mode_t mask; |
mode_t mask; |
12563 |
int i; |
int i; |
12600 |
mask = 0; |
mask = 0; |
12601 |
do { |
do { |
12602 |
if (*ap >= '8' || *ap < '0') |
if (*ap >= '8' || *ap < '0') |
12603 |
ash_msg_and_raise_error(illnum, argv[1]); |
ash_msg_and_raise_error(msg_illnum, argv[1]); |
12604 |
mask = (mask << 3) + (*ap - '0'); |
mask = (mask << 3) + (*ap - '0'); |
12605 |
} while (*++ap != '\0'); |
} while (*++ap != '\0'); |
12606 |
umask(mask); |
umask(mask); |
12624 |
* |
* |
12625 |
* Public domain. |
* Public domain. |
12626 |
*/ |
*/ |
|
|
|
12627 |
struct limits { |
struct limits { |
12628 |
uint8_t cmd; /* RLIMIT_xxx fit into it */ |
uint8_t cmd; /* RLIMIT_xxx fit into it */ |
12629 |
uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ |
uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ |
12721 |
} |
} |
12722 |
} |
} |
12723 |
|
|
12724 |
static int |
static int FAST_FUNC |
12725 |
ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
12726 |
{ |
{ |
12727 |
int c; |
rlim_t val; |
|
rlim_t val = 0; |
|
12728 |
enum limtype how = SOFT | HARD; |
enum limtype how = SOFT | HARD; |
12729 |
const struct limits *l; |
const struct limits *l; |
12730 |
int set, all = 0; |
int set, all = 0; |
12785 |
continue; |
continue; |
12786 |
|
|
12787 |
set = *argptr ? 1 : 0; |
set = *argptr ? 1 : 0; |
12788 |
|
val = 0; |
12789 |
if (set) { |
if (set) { |
12790 |
char *p = *argptr; |
char *p = *argptr; |
12791 |
|
|
12794 |
if (strncmp(p, "unlimited\n", 9) == 0) |
if (strncmp(p, "unlimited\n", 9) == 0) |
12795 |
val = RLIM_INFINITY; |
val = RLIM_INFINITY; |
12796 |
else { |
else { |
12797 |
val = (rlim_t) 0; |
if (sizeof(val) == sizeof(int)) |
12798 |
|
val = bb_strtou(p, NULL, 10); |
12799 |
while ((c = *p++) >= '0' && c <= '9') { |
else if (sizeof(val) == sizeof(long)) |
12800 |
val = (val * 10) + (long)(c - '0'); |
val = bb_strtoul(p, NULL, 10); |
12801 |
// val is actually 'unsigned long int' and can't get < 0 |
else |
12802 |
if (val < (rlim_t) 0) |
val = bb_strtoull(p, NULL, 10); |
12803 |
break; |
if (errno) |
|
} |
|
|
if (c) |
|
12804 |
ash_msg_and_raise_error("bad number"); |
ash_msg_and_raise_error("bad number"); |
12805 |
val <<= l->factor_shift; |
val <<= l->factor_shift; |
12806 |
} |
} |
12830 |
return 0; |
return 0; |
12831 |
} |
} |
12832 |
|
|
|
|
|
|
/* ============ Math support */ |
|
|
|
|
|
#if ENABLE_ASH_MATH_SUPPORT |
|
|
|
|
|
/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com> |
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining |
|
|
a copy of this software and associated documentation files (the |
|
|
"Software"), to deal in the Software without restriction, including |
|
|
without limitation the rights to use, copy, modify, merge, publish, |
|
|
distribute, sublicense, and/or sell copies of the Software, and to |
|
|
permit persons to whom the Software is furnished to do so, subject to |
|
|
the following conditions: |
|
|
|
|
|
The above copyright notice and this permission notice shall be |
|
|
included in all copies or substantial portions of the Software. |
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
|
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
|
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
|
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
|
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
|
*/ |
|
|
|
|
|
/* This is my infix parser/evaluator. It is optimized for size, intended |
|
|
* as a replacement for yacc-based parsers. However, it may well be faster |
|
|
* than a comparable parser written in yacc. The supported operators are |
|
|
* listed in #defines below. Parens, order of operations, and error handling |
|
|
* are supported. This code is thread safe. The exact expression format should |
|
|
* be that which POSIX specifies for shells. */ |
|
|
|
|
|
/* The code uses a simple two-stack algorithm. See |
|
|
* http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html |
|
|
* for a detailed explanation of the infix-to-postfix algorithm on which |
|
|
* this is based (this code differs in that it applies operators immediately |
|
|
* to the stack instead of adding them to a queue to end up with an |
|
|
* expression). */ |
|
|
|
|
|
/* To use the routine, call it with an expression string and error return |
|
|
* pointer */ |
|
|
|
|
|
/* |
|
|
* Aug 24, 2001 Manuel Novoa III |
|
|
* |
|
|
* Reduced the generated code size by about 30% (i386) and fixed several bugs. |
|
|
* |
|
|
* 1) In arith_apply(): |
|
|
* a) Cached values of *numptr and &(numptr[-1]). |
|
|
* b) Removed redundant test for zero denominator. |
|
|
* |
|
|
* 2) In arith(): |
|
|
* a) Eliminated redundant code for processing operator tokens by moving |
|
|
* to a table-based implementation. Also folded handling of parens |
|
|
* into the table. |
|
|
* b) Combined all 3 loops which called arith_apply to reduce generated |
|
|
* code size at the cost of speed. |
|
|
* |
|
|
* 3) The following expressions were treated as valid by the original code: |
|
|
* 1() , 0! , 1 ( *3 ) . |
|
|
* These bugs have been fixed by internally enclosing the expression in |
|
|
* parens and then checking that all binary ops and right parens are |
|
|
* preceded by a valid expression (NUM_TOKEN). |
|
|
* |
|
|
* Note: It may be desirable to replace Aaron's test for whitespace with |
|
|
* ctype's isspace() if it is used by another busybox applet or if additional |
|
|
* whitespace chars should be considered. Look below the "#include"s for a |
|
|
* precompiler test. |
|
|
*/ |
|
|
|
|
|
/* |
|
|
* Aug 26, 2001 Manuel Novoa III |
|
|
* |
|
|
* Return 0 for null expressions. Pointed out by Vladimir Oleynik. |
|
|
* |
|
|
* Merge in Aaron's comments previously posted to the busybox list, |
|
|
* modified slightly to take account of my changes to the code. |
|
|
* |
|
|
*/ |
|
|
|
|
|
/* |
|
|
* (C) 2003 Vladimir Oleynik <dzo@simtreas.ru> |
|
|
* |
|
|
* - allow access to variable, |
|
|
* used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6) |
|
|
* - realize assign syntax (VAR=expr, +=, *= etc) |
|
|
* - realize exponentiation (** operator) |
|
|
* - realize comma separated - expr, expr |
|
|
* - realise ++expr --expr expr++ expr-- |
|
|
* - realise expr ? expr : expr (but, second expr calculate always) |
|
|
* - allow hexadecimal and octal numbers |
|
|
* - was restored loses XOR operator |
|
|
* - remove one goto label, added three ;-) |
|
|
* - protect $((num num)) as true zero expr (Manuel`s error) |
|
|
* - always use special isspace(), see comment from bash ;-) |
|
|
*/ |
|
|
|
|
|
#define arith_isspace(arithval) \ |
|
|
(arithval == ' ' || arithval == '\n' || arithval == '\t') |
|
|
|
|
|
typedef unsigned char operator; |
|
|
|
|
|
/* An operator's token id is a bit of a bitfield. The lower 5 bits are the |
|
|
* precedence, and 3 high bits are an ID unique across operators of that |
|
|
* precedence. The ID portion is so that multiple operators can have the |
|
|
* same precedence, ensuring that the leftmost one is evaluated first. |
|
|
* Consider * and /. */ |
|
|
|
|
|
#define tok_decl(prec,id) (((id)<<5)|(prec)) |
|
|
#define PREC(op) ((op) & 0x1F) |
|
|
|
|
|
#define TOK_LPAREN tok_decl(0,0) |
|
|
|
|
|
#define TOK_COMMA tok_decl(1,0) |
|
|
|
|
|
#define TOK_ASSIGN tok_decl(2,0) |
|
|
#define TOK_AND_ASSIGN tok_decl(2,1) |
|
|
#define TOK_OR_ASSIGN tok_decl(2,2) |
|
|
#define TOK_XOR_ASSIGN tok_decl(2,3) |
|
|
#define TOK_PLUS_ASSIGN tok_decl(2,4) |
|
|
#define TOK_MINUS_ASSIGN tok_decl(2,5) |
|
|
#define TOK_LSHIFT_ASSIGN tok_decl(2,6) |
|
|
#define TOK_RSHIFT_ASSIGN tok_decl(2,7) |
|
|
|
|
|
#define TOK_MUL_ASSIGN tok_decl(3,0) |
|
|
#define TOK_DIV_ASSIGN tok_decl(3,1) |
|
|
#define TOK_REM_ASSIGN tok_decl(3,2) |
|
|
|
|
|
/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */ |
|
|
#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0) |
|
|
|
|
|
/* conditional is right associativity too */ |
|
|
#define TOK_CONDITIONAL tok_decl(4,0) |
|
|
#define TOK_CONDITIONAL_SEP tok_decl(4,1) |
|
|
|
|
|
#define TOK_OR tok_decl(5,0) |
|
|
|
|
|
#define TOK_AND tok_decl(6,0) |
|
|
|
|
|
#define TOK_BOR tok_decl(7,0) |
|
|
|
|
|
#define TOK_BXOR tok_decl(8,0) |
|
|
|
|
|
#define TOK_BAND tok_decl(9,0) |
|
|
|
|
|
#define TOK_EQ tok_decl(10,0) |
|
|
#define TOK_NE tok_decl(10,1) |
|
|
|
|
|
#define TOK_LT tok_decl(11,0) |
|
|
#define TOK_GT tok_decl(11,1) |
|
|
#define TOK_GE tok_decl(11,2) |
|
|
#define TOK_LE tok_decl(11,3) |
|
|
|
|
|
#define TOK_LSHIFT tok_decl(12,0) |
|
|
#define TOK_RSHIFT tok_decl(12,1) |
|
|
|
|
|
#define TOK_ADD tok_decl(13,0) |
|
|
#define TOK_SUB tok_decl(13,1) |
|
|
|
|
|
#define TOK_MUL tok_decl(14,0) |
|
|
#define TOK_DIV tok_decl(14,1) |
|
|
#define TOK_REM tok_decl(14,2) |
|
|
|
|
|
/* exponent is right associativity */ |
|
|
#define TOK_EXPONENT tok_decl(15,1) |
|
|
|
|
|
/* For now unary operators. */ |
|
|
#define UNARYPREC 16 |
|
|
#define TOK_BNOT tok_decl(UNARYPREC,0) |
|
|
#define TOK_NOT tok_decl(UNARYPREC,1) |
|
|
|
|
|
#define TOK_UMINUS tok_decl(UNARYPREC+1,0) |
|
|
#define TOK_UPLUS tok_decl(UNARYPREC+1,1) |
|
|
|
|
|
#define PREC_PRE (UNARYPREC+2) |
|
|
|
|
|
#define TOK_PRE_INC tok_decl(PREC_PRE, 0) |
|
|
#define TOK_PRE_DEC tok_decl(PREC_PRE, 1) |
|
|
|
|
|
#define PREC_POST (UNARYPREC+3) |
|
|
|
|
|
#define TOK_POST_INC tok_decl(PREC_POST, 0) |
|
|
#define TOK_POST_DEC tok_decl(PREC_POST, 1) |
|
|
|
|
|
#define SPEC_PREC (UNARYPREC+4) |
|
|
|
|
|
#define TOK_NUM tok_decl(SPEC_PREC, 0) |
|
|
#define TOK_RPAREN tok_decl(SPEC_PREC, 1) |
|
|
|
|
|
#define NUMPTR (*numstackptr) |
|
|
|
|
|
static int |
|
|
tok_have_assign(operator op) |
|
|
{ |
|
|
operator prec = PREC(op); |
|
|
|
|
|
convert_prec_is_assing(prec); |
|
|
return (prec == PREC(TOK_ASSIGN) || |
|
|
prec == PREC_PRE || prec == PREC_POST); |
|
|
} |
|
|
|
|
|
static int |
|
|
is_right_associativity(operator prec) |
|
|
{ |
|
|
return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT) |
|
|
|| prec == PREC(TOK_CONDITIONAL)); |
|
|
} |
|
|
|
|
|
typedef struct { |
|
|
arith_t val; |
|
|
arith_t contidional_second_val; |
|
|
char contidional_second_val_initialized; |
|
|
char *var; /* if NULL then is regular number, |
|
|
else is variable name */ |
|
|
} v_n_t; |
|
|
|
|
|
typedef struct chk_var_recursive_looped_t { |
|
|
const char *var; |
|
|
struct chk_var_recursive_looped_t *next; |
|
|
} chk_var_recursive_looped_t; |
|
|
|
|
|
static chk_var_recursive_looped_t *prev_chk_var_recursive; |
|
|
|
|
|
static int |
|
|
arith_lookup_val(v_n_t *t) |
|
|
{ |
|
|
if (t->var) { |
|
|
const char * p = lookupvar(t->var); |
|
|
|
|
|
if (p) { |
|
|
int errcode; |
|
|
|
|
|
/* recursive try as expression */ |
|
|
chk_var_recursive_looped_t *cur; |
|
|
chk_var_recursive_looped_t cur_save; |
|
|
|
|
|
for (cur = prev_chk_var_recursive; cur; cur = cur->next) { |
|
|
if (strcmp(cur->var, t->var) == 0) { |
|
|
/* expression recursion loop detected */ |
|
|
return -5; |
|
|
} |
|
|
} |
|
|
/* save current lookuped var name */ |
|
|
cur = prev_chk_var_recursive; |
|
|
cur_save.var = t->var; |
|
|
cur_save.next = cur; |
|
|
prev_chk_var_recursive = &cur_save; |
|
|
|
|
|
t->val = arith (p, &errcode); |
|
|
/* restore previous ptr after recursiving */ |
|
|
prev_chk_var_recursive = cur; |
|
|
return errcode; |
|
|
} |
|
|
/* allow undefined var as 0 */ |
|
|
t->val = 0; |
|
|
} |
|
|
return 0; |
|
|
} |
|
|
|
|
|
/* "applying" a token means performing it on the top elements on the integer |
|
|
* stack. For a unary operator it will only change the top element, but a |
|
|
* binary operator will pop two arguments and push a result */ |
|
|
static int |
|
|
arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr) |
|
|
{ |
|
|
v_n_t *numptr_m1; |
|
|
arith_t numptr_val, rez; |
|
|
int ret_arith_lookup_val; |
|
|
|
|
|
/* There is no operator that can work without arguments */ |
|
|
if (NUMPTR == numstack) goto err; |
|
|
numptr_m1 = NUMPTR - 1; |
|
|
|
|
|
/* check operand is var with noninteger value */ |
|
|
ret_arith_lookup_val = arith_lookup_val(numptr_m1); |
|
|
if (ret_arith_lookup_val) |
|
|
return ret_arith_lookup_val; |
|
|
|
|
|
rez = numptr_m1->val; |
|
|
if (op == TOK_UMINUS) |
|
|
rez *= -1; |
|
|
else if (op == TOK_NOT) |
|
|
rez = !rez; |
|
|
else if (op == TOK_BNOT) |
|
|
rez = ~rez; |
|
|
else if (op == TOK_POST_INC || op == TOK_PRE_INC) |
|
|
rez++; |
|
|
else if (op == TOK_POST_DEC || op == TOK_PRE_DEC) |
|
|
rez--; |
|
|
else if (op != TOK_UPLUS) { |
|
|
/* Binary operators */ |
|
|
|
|
|
/* check and binary operators need two arguments */ |
|
|
if (numptr_m1 == numstack) goto err; |
|
|
|
|
|
/* ... and they pop one */ |
|
|
--NUMPTR; |
|
|
numptr_val = rez; |
|
|
if (op == TOK_CONDITIONAL) { |
|
|
if (!numptr_m1->contidional_second_val_initialized) { |
|
|
/* protect $((expr1 ? expr2)) without ": expr" */ |
|
|
goto err; |
|
|
} |
|
|
rez = numptr_m1->contidional_second_val; |
|
|
} else if (numptr_m1->contidional_second_val_initialized) { |
|
|
/* protect $((expr1 : expr2)) without "expr ? " */ |
|
|
goto err; |
|
|
} |
|
|
numptr_m1 = NUMPTR - 1; |
|
|
if (op != TOK_ASSIGN) { |
|
|
/* check operand is var with noninteger value for not '=' */ |
|
|
ret_arith_lookup_val = arith_lookup_val(numptr_m1); |
|
|
if (ret_arith_lookup_val) |
|
|
return ret_arith_lookup_val; |
|
|
} |
|
|
if (op == TOK_CONDITIONAL) { |
|
|
numptr_m1->contidional_second_val = rez; |
|
|
} |
|
|
rez = numptr_m1->val; |
|
|
if (op == TOK_BOR || op == TOK_OR_ASSIGN) |
|
|
rez |= numptr_val; |
|
|
else if (op == TOK_OR) |
|
|
rez = numptr_val || rez; |
|
|
else if (op == TOK_BAND || op == TOK_AND_ASSIGN) |
|
|
rez &= numptr_val; |
|
|
else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN) |
|
|
rez ^= numptr_val; |
|
|
else if (op == TOK_AND) |
|
|
rez = rez && numptr_val; |
|
|
else if (op == TOK_EQ) |
|
|
rez = (rez == numptr_val); |
|
|
else if (op == TOK_NE) |
|
|
rez = (rez != numptr_val); |
|
|
else if (op == TOK_GE) |
|
|
rez = (rez >= numptr_val); |
|
|
else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN) |
|
|
rez >>= numptr_val; |
|
|
else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN) |
|
|
rez <<= numptr_val; |
|
|
else if (op == TOK_GT) |
|
|
rez = (rez > numptr_val); |
|
|
else if (op == TOK_LT) |
|
|
rez = (rez < numptr_val); |
|
|
else if (op == TOK_LE) |
|
|
rez = (rez <= numptr_val); |
|
|
else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) |
|
|
rez *= numptr_val; |
|
|
else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN) |
|
|
rez += numptr_val; |
|
|
else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN) |
|
|
rez -= numptr_val; |
|
|
else if (op == TOK_ASSIGN || op == TOK_COMMA) |
|
|
rez = numptr_val; |
|
|
else if (op == TOK_CONDITIONAL_SEP) { |
|
|
if (numptr_m1 == numstack) { |
|
|
/* protect $((expr : expr)) without "expr ? " */ |
|
|
goto err; |
|
|
} |
|
|
numptr_m1->contidional_second_val_initialized = op; |
|
|
numptr_m1->contidional_second_val = numptr_val; |
|
|
} else if (op == TOK_CONDITIONAL) { |
|
|
rez = rez ? |
|
|
numptr_val : numptr_m1->contidional_second_val; |
|
|
} else if (op == TOK_EXPONENT) { |
|
|
if (numptr_val < 0) |
|
|
return -3; /* exponent less than 0 */ |
|
|
else { |
|
|
arith_t c = 1; |
|
|
|
|
|
if (numptr_val) |
|
|
while (numptr_val--) |
|
|
c *= rez; |
|
|
rez = c; |
|
|
} |
|
|
} else if (numptr_val==0) /* zero divisor check */ |
|
|
return -2; |
|
|
else if (op == TOK_DIV || op == TOK_DIV_ASSIGN) |
|
|
rez /= numptr_val; |
|
|
else if (op == TOK_REM || op == TOK_REM_ASSIGN) |
|
|
rez %= numptr_val; |
|
|
} |
|
|
if (tok_have_assign(op)) { |
|
|
char buf[sizeof(arith_t_type)*3 + 2]; |
|
|
|
|
|
if (numptr_m1->var == NULL) { |
|
|
/* Hmm, 1=2 ? */ |
|
|
goto err; |
|
|
} |
|
|
/* save to shell variable */ |
|
|
#if ENABLE_ASH_MATH_SUPPORT_64 |
|
|
snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez); |
|
|
#else |
|
|
snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez); |
|
|
#endif |
|
|
setvar(numptr_m1->var, buf, 0); |
|
|
/* after saving, make previous value for v++ or v-- */ |
|
|
if (op == TOK_POST_INC) |
|
|
rez--; |
|
|
else if (op == TOK_POST_DEC) |
|
|
rez++; |
|
|
} |
|
|
numptr_m1->val = rez; |
|
|
/* protect geting var value, is number now */ |
|
|
numptr_m1->var = NULL; |
|
|
return 0; |
|
|
err: |
|
|
return -1; |
|
|
} |
|
|
|
|
|
/* longest must be first */ |
|
|
static const char op_tokens[] ALIGN1 = { |
|
|
'<','<','=',0, TOK_LSHIFT_ASSIGN, |
|
|
'>','>','=',0, TOK_RSHIFT_ASSIGN, |
|
|
'<','<', 0, TOK_LSHIFT, |
|
|
'>','>', 0, TOK_RSHIFT, |
|
|
'|','|', 0, TOK_OR, |
|
|
'&','&', 0, TOK_AND, |
|
|
'!','=', 0, TOK_NE, |
|
|
'<','=', 0, TOK_LE, |
|
|
'>','=', 0, TOK_GE, |
|
|
'=','=', 0, TOK_EQ, |
|
|
'|','=', 0, TOK_OR_ASSIGN, |
|
|
'&','=', 0, TOK_AND_ASSIGN, |
|
|
'*','=', 0, TOK_MUL_ASSIGN, |
|
|
'/','=', 0, TOK_DIV_ASSIGN, |
|
|
'%','=', 0, TOK_REM_ASSIGN, |
|
|
'+','=', 0, TOK_PLUS_ASSIGN, |
|
|
'-','=', 0, TOK_MINUS_ASSIGN, |
|
|
'-','-', 0, TOK_POST_DEC, |
|
|
'^','=', 0, TOK_XOR_ASSIGN, |
|
|
'+','+', 0, TOK_POST_INC, |
|
|
'*','*', 0, TOK_EXPONENT, |
|
|
'!', 0, TOK_NOT, |
|
|
'<', 0, TOK_LT, |
|
|
'>', 0, TOK_GT, |
|
|
'=', 0, TOK_ASSIGN, |
|
|
'|', 0, TOK_BOR, |
|
|
'&', 0, TOK_BAND, |
|
|
'*', 0, TOK_MUL, |
|
|
'/', 0, TOK_DIV, |
|
|
'%', 0, TOK_REM, |
|
|
'+', 0, TOK_ADD, |
|
|
'-', 0, TOK_SUB, |
|
|
'^', 0, TOK_BXOR, |
|
|
/* uniq */ |
|
|
'~', 0, TOK_BNOT, |
|
|
',', 0, TOK_COMMA, |
|
|
'?', 0, TOK_CONDITIONAL, |
|
|
':', 0, TOK_CONDITIONAL_SEP, |
|
|
')', 0, TOK_RPAREN, |
|
|
'(', 0, TOK_LPAREN, |
|
|
0 |
|
|
}; |
|
|
/* ptr to ")" */ |
|
|
#define endexpression (&op_tokens[sizeof(op_tokens)-7]) |
|
|
|
|
|
static arith_t |
|
|
arith(const char *expr, int *perrcode) |
|
|
{ |
|
|
char arithval; /* Current character under analysis */ |
|
|
operator lasttok, op; |
|
|
operator prec; |
|
|
operator *stack, *stackptr; |
|
|
const char *p = endexpression; |
|
|
int errcode; |
|
|
v_n_t *numstack, *numstackptr; |
|
|
unsigned datasizes = strlen(expr) + 2; |
|
|
|
|
|
/* Stack of integers */ |
|
|
/* The proof that there can be no more than strlen(startbuf)/2+1 integers |
|
|
* in any given correct or incorrect expression is left as an exercise to |
|
|
* the reader. */ |
|
|
numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0])); |
|
|
/* Stack of operator tokens */ |
|
|
stackptr = stack = alloca(datasizes * sizeof(stack[0])); |
|
|
|
|
|
*stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */ |
|
|
*perrcode = errcode = 0; |
|
|
|
|
|
while (1) { |
|
|
arithval = *expr; |
|
|
if (arithval == 0) { |
|
|
if (p == endexpression) { |
|
|
/* Null expression. */ |
|
|
return 0; |
|
|
} |
|
|
|
|
|
/* This is only reached after all tokens have been extracted from the |
|
|
* input stream. If there are still tokens on the operator stack, they |
|
|
* are to be applied in order. At the end, there should be a final |
|
|
* result on the integer stack */ |
|
|
|
|
|
if (expr != endexpression + 1) { |
|
|
/* If we haven't done so already, */ |
|
|
/* append a closing right paren */ |
|
|
expr = endexpression; |
|
|
/* and let the loop process it. */ |
|
|
continue; |
|
|
} |
|
|
/* At this point, we're done with the expression. */ |
|
|
if (numstackptr != numstack+1) { |
|
|
/* ... but if there isn't, it's bad */ |
|
|
err: |
|
|
*perrcode = -1; |
|
|
return *perrcode; |
|
|
} |
|
|
if (numstack->var) { |
|
|
/* expression is $((var)) only, lookup now */ |
|
|
errcode = arith_lookup_val(numstack); |
|
|
} |
|
|
ret: |
|
|
*perrcode = errcode; |
|
|
return numstack->val; |
|
|
} |
|
|
|
|
|
/* Continue processing the expression. */ |
|
|
if (arith_isspace(arithval)) { |
|
|
/* Skip whitespace */ |
|
|
goto prologue; |
|
|
} |
|
|
p = endofname(expr); |
|
|
if (p != expr) { |
|
|
size_t var_name_size = (p-expr) + 1; /* trailing zero */ |
|
|
|
|
|
numstackptr->var = alloca(var_name_size); |
|
|
safe_strncpy(numstackptr->var, expr, var_name_size); |
|
|
expr = p; |
|
|
num: |
|
|
numstackptr->contidional_second_val_initialized = 0; |
|
|
numstackptr++; |
|
|
lasttok = TOK_NUM; |
|
|
continue; |
|
|
} |
|
|
if (isdigit(arithval)) { |
|
|
numstackptr->var = NULL; |
|
|
#if ENABLE_ASH_MATH_SUPPORT_64 |
|
|
numstackptr->val = strtoll(expr, (char **) &expr, 0); |
|
|
#else |
|
|
numstackptr->val = strtol(expr, (char **) &expr, 0); |
|
|
#endif |
|
|
goto num; |
|
|
} |
|
|
for (p = op_tokens; ; p++) { |
|
|
const char *o; |
|
|
|
|
|
if (*p == 0) { |
|
|
/* strange operator not found */ |
|
|
goto err; |
|
|
} |
|
|
for (o = expr; *p && *o == *p; p++) |
|
|
o++; |
|
|
if (!*p) { |
|
|
/* found */ |
|
|
expr = o - 1; |
|
|
break; |
|
|
} |
|
|
/* skip tail uncompared token */ |
|
|
while (*p) |
|
|
p++; |
|
|
/* skip zero delim */ |
|
|
p++; |
|
|
} |
|
|
op = p[1]; |
|
|
|
|
|
/* post grammar: a++ reduce to num */ |
|
|
if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC) |
|
|
lasttok = TOK_NUM; |
|
|
|
|
|
/* Plus and minus are binary (not unary) _only_ if the last |
|
|
* token was as number, or a right paren (which pretends to be |
|
|
* a number, since it evaluates to one). Think about it. |
|
|
* It makes sense. */ |
|
|
if (lasttok != TOK_NUM) { |
|
|
switch (op) { |
|
|
case TOK_ADD: |
|
|
op = TOK_UPLUS; |
|
|
break; |
|
|
case TOK_SUB: |
|
|
op = TOK_UMINUS; |
|
|
break; |
|
|
case TOK_POST_INC: |
|
|
op = TOK_PRE_INC; |
|
|
break; |
|
|
case TOK_POST_DEC: |
|
|
op = TOK_PRE_DEC; |
|
|
break; |
|
|
} |
|
|
} |
|
|
/* We don't want a unary operator to cause recursive descent on the |
|
|
* stack, because there can be many in a row and it could cause an |
|
|
* operator to be evaluated before its argument is pushed onto the |
|
|
* integer stack. */ |
|
|
/* But for binary operators, "apply" everything on the operator |
|
|
* stack until we find an operator with a lesser priority than the |
|
|
* one we have just extracted. */ |
|
|
/* Left paren is given the lowest priority so it will never be |
|
|
* "applied" in this way. |
|
|
* if associativity is right and priority eq, applied also skip |
|
|
*/ |
|
|
prec = PREC(op); |
|
|
if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) { |
|
|
/* not left paren or unary */ |
|
|
if (lasttok != TOK_NUM) { |
|
|
/* binary op must be preceded by a num */ |
|
|
goto err; |
|
|
} |
|
|
while (stackptr != stack) { |
|
|
if (op == TOK_RPAREN) { |
|
|
/* The algorithm employed here is simple: while we don't |
|
|
* hit an open paren nor the bottom of the stack, pop |
|
|
* tokens and apply them */ |
|
|
if (stackptr[-1] == TOK_LPAREN) { |
|
|
--stackptr; |
|
|
/* Any operator directly after a */ |
|
|
lasttok = TOK_NUM; |
|
|
/* close paren should consider itself binary */ |
|
|
goto prologue; |
|
|
} |
|
|
} else { |
|
|
operator prev_prec = PREC(stackptr[-1]); |
|
|
|
|
|
convert_prec_is_assing(prec); |
|
|
convert_prec_is_assing(prev_prec); |
|
|
if (prev_prec < prec) |
|
|
break; |
|
|
/* check right assoc */ |
|
|
if (prev_prec == prec && is_right_associativity(prec)) |
|
|
break; |
|
|
} |
|
|
errcode = arith_apply(*--stackptr, numstack, &numstackptr); |
|
|
if (errcode) goto ret; |
|
|
} |
|
|
if (op == TOK_RPAREN) { |
|
|
goto err; |
|
|
} |
|
|
} |
|
|
|
|
|
/* Push this operator to the stack and remember it. */ |
|
|
*stackptr++ = lasttok = op; |
|
|
prologue: |
|
|
++expr; |
|
|
} /* while */ |
|
|
} |
|
|
#endif /* ASH_MATH_SUPPORT */ |
|
|
|
|
|
|
|
12833 |
/* ============ main() and helpers */ |
/* ============ main() and helpers */ |
12834 |
|
|
12835 |
/* |
/* |
12846 |
status = exitstatus; |
status = exitstatus; |
12847 |
TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); |
TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); |
12848 |
if (setjmp(loc.loc)) { |
if (setjmp(loc.loc)) { |
12849 |
if (exception == EXEXIT) |
if (exception_type == EXEXIT) |
12850 |
/* dash bug: it just does _exit(exitstatus) here |
/* dash bug: it just does _exit(exitstatus) here |
12851 |
* but we have to do setjobctl(0) first! |
* but we have to do setjobctl(0) first! |
12852 |
* (bug is still not fixed in dash-0.5.3 - if you run dash |
* (bug is still not fixed in dash-0.5.3 - if you run dash |
12859 |
if (p) { |
if (p) { |
12860 |
trap[0] = NULL; |
trap[0] = NULL; |
12861 |
evalstring(p, 0); |
evalstring(p, 0); |
12862 |
|
free(p); |
12863 |
} |
} |
12864 |
flush_stdout_stderr(); |
flush_stdout_stderr(); |
12865 |
out: |
out: |
12872 |
init(void) |
init(void) |
12873 |
{ |
{ |
12874 |
/* from input.c: */ |
/* from input.c: */ |
12875 |
basepf.nextc = basepf.buf = basebuf; |
basepf.next_to_pgetc = basepf.buf = basebuf; |
12876 |
|
|
12877 |
/* from trap.c: */ |
/* from trap.c: */ |
12878 |
signal(SIGCHLD, SIG_DFL); |
signal(SIGCHLD, SIG_DFL); |
12879 |
|
/* bash re-enables SIGHUP which is SIG_IGNed on entry. |
12880 |
|
* Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" |
12881 |
|
*/ |
12882 |
|
signal(SIGHUP, SIG_DFL); |
12883 |
|
|
12884 |
/* from var.c: */ |
/* from var.c: */ |
12885 |
{ |
{ |
12886 |
char **envp; |
char **envp; |
|
char ppid[sizeof(int)*3 + 1]; |
|
12887 |
const char *p; |
const char *p; |
12888 |
struct stat st1, st2; |
struct stat st1, st2; |
12889 |
|
|
12894 |
} |
} |
12895 |
} |
} |
12896 |
|
|
12897 |
snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid()); |
setvar("PPID", utoa(getppid()), 0); |
|
setvar("PPID", ppid, 0); |
|
12898 |
|
|
12899 |
p = lookupvar("PWD"); |
p = lookupvar("PWD"); |
12900 |
if (p) |
if (p) |
12995 |
evalskip = 0; |
evalskip = 0; |
12996 |
loopnest = 0; |
loopnest = 0; |
12997 |
/* from input.c: */ |
/* from input.c: */ |
12998 |
parselleft = parsenleft = 0; /* clear input buffer */ |
g_parsefile->left_in_buffer = 0; |
12999 |
|
g_parsefile->left_in_line = 0; /* clear input buffer */ |
13000 |
popallfiles(); |
popallfiles(); |
13001 |
/* from parser.c: */ |
/* from parser.c: */ |
13002 |
tokpushback = 0; |
tokpushback = 0; |
13020 |
int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
13021 |
int ash_main(int argc UNUSED_PARAM, char **argv) |
int ash_main(int argc UNUSED_PARAM, char **argv) |
13022 |
{ |
{ |
13023 |
char *shinit; |
const char *shinit; |
13024 |
volatile int state; |
volatile smallint state; |
13025 |
struct jmploc jmploc; |
struct jmploc jmploc; |
13026 |
struct stackmark smark; |
struct stackmark smark; |
13027 |
|
|
13043 |
#endif |
#endif |
13044 |
state = 0; |
state = 0; |
13045 |
if (setjmp(jmploc.loc)) { |
if (setjmp(jmploc.loc)) { |
13046 |
int e; |
smallint e; |
13047 |
int s; |
smallint s; |
13048 |
|
|
13049 |
reset(); |
reset(); |
13050 |
|
|
13051 |
e = exception; |
e = exception_type; |
13052 |
if (e == EXERROR) |
if (e == EXERROR) |
13053 |
exitstatus = 2; |
exitstatus = 2; |
13054 |
s = state; |
s = state; |
13055 |
if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) |
if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) |
13056 |
exitshell(); |
exitshell(); |
13057 |
|
if (e == EXINT) |
|
if (e == EXINT) { |
|
13058 |
outcslow('\n', stderr); |
outcslow('\n', stderr); |
13059 |
} |
|
13060 |
popstackmark(&smark); |
popstackmark(&smark); |
13061 |
FORCE_INT_ON; /* enable interrupts */ |
FORCE_INT_ON; /* enable interrupts */ |
13062 |
if (s == 1) |
if (s == 1) |
13075 |
#endif |
#endif |
13076 |
rootpid = getpid(); |
rootpid = getpid(); |
13077 |
|
|
|
#if ENABLE_ASH_RANDOM_SUPPORT |
|
|
/* Can use monotonic_ns() for better randomness but for now it is |
|
|
* not used anywhere else in busybox... so avoid bloat */ |
|
|
random_galois_LFSR = random_LCG = rootpid + monotonic_us(); |
|
|
#endif |
|
13078 |
init(); |
init(); |
13079 |
setstackmark(&smark); |
setstackmark(&smark); |
13080 |
procargs(argv); |
procargs(argv); |
13093 |
} |
} |
13094 |
} |
} |
13095 |
#endif |
#endif |
13096 |
if (argv[0] && argv[0][0] == '-') |
if (/* argv[0] && */ argv[0][0] == '-') |
13097 |
isloginsh = 1; |
isloginsh = 1; |
13098 |
if (isloginsh) { |
if (isloginsh) { |
13099 |
state = 1; |
state = 1; |
13120 |
if (minusc) { |
if (minusc) { |
13121 |
/* evalstring pushes parsefile stack. |
/* evalstring pushes parsefile stack. |
13122 |
* Ensure we don't falsely claim that 0 (stdin) |
* Ensure we don't falsely claim that 0 (stdin) |
13123 |
* is one of stacked source fds */ |
* is one of stacked source fds. |
13124 |
|
* Testcase: ash -c 'exec 1>&0' must not complain. */ |
13125 |
if (!sflag) |
if (!sflag) |
13126 |
g_parsefile->fd = -1; |
g_parsefile->fd = -1; |
13127 |
evalstring(minusc, 0); |
evalstring(minusc, 0); |
13128 |
} |
} |
13129 |
|
|
13130 |
if (sflag || minusc == NULL) { |
if (sflag || minusc == NULL) { |
13131 |
#if ENABLE_FEATURE_EDITING_SAVEHISTORY |
#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY |
13132 |
if (iflag) { |
if (iflag) { |
13133 |
const char *hp = lookupvar("HISTFILE"); |
const char *hp = lookupvar("HISTFILE"); |
13134 |
|
if (hp) |
|
if (hp != NULL) |
|
13135 |
line_input_state->hist_file = hp; |
line_input_state->hist_file = hp; |
13136 |
} |
} |
13137 |
#endif |
#endif |