ash: cleanup part 1
No binary (build failed)
—
(log yok)
| @@ -503,109 +503,392 @@ errmsg(int e, const char *em) | |
| } | |
| - | /* ============ Unsorted yet */ |
| - | |
| + | /* ============ Memory allocation */ |
| - | static void setpwd(const char *, int); |
| + | /* |
| + | * It appears that grabstackstr() will barf with such alignments |
| + | * because stalloc() will return a string allocated in a new stackblock. |
| + | */ |
| + | #define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE) |
| + | enum { |
| + | /* Most machines require the value returned from malloc to be aligned |
| + | * in some way. The following macro will get this right |
| + | * on many machines. */ |
| + | SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1, |
| + | /* Minimum size of a block */ |
| + | MINSIZE = SHELL_ALIGN(504), |
| + | }; |
| - | /* expand.h */ |
| + | struct stack_block { |
| + | struct stack_block *prev; |
| + | char space[MINSIZE]; |
| + | }; |
| - | struct strlist { |
| - | struct strlist *next; |
| - | char *text; |
| + | struct stackmark { |
| + | struct stack_block *stackp; |
| + | char *stacknxt; |
| + | size_t stacknleft; |
| + | struct stackmark *marknext; |
| }; | |
| + | static struct stack_block stackbase; |
| + | static struct stack_block *stackp = &stackbase; |
| + | static struct stackmark *markp; |
| + | static char *stacknxt = stackbase.space; |
| + | static size_t stacknleft = MINSIZE; |
| + | static char *sstrend = stackbase.space + MINSIZE; |
| + | static int herefd = -1; |
| - | struct arglist { |
| - | struct strlist *list; |
| - | struct strlist **lastp; |
| - | }; |
| + | #define stackblock() ((void *)stacknxt) |
| + | #define stackblocksize() stacknleft |
| + | |
| + | 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); |
| + | } |
| /* | |
| - | * expandarg() flags |
| + | * Make a copy of a string in safe storage. |
| */ | |
| - | #define EXP_FULL 0x1 /* perform word splitting & file globbing */ |
| - | #define EXP_TILDE 0x2 /* do normal tilde expansion */ |
| - | #define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ |
| - | #define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ |
| - | #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ |
| - | #define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */ |
| - | #define EXP_VARTILDE2 0x40 /* expand tildes after colons only */ |
| - | #define EXP_WORD 0x80 /* expand word in parameter expansion */ |
| - | #define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */ |
| + | static char * |
| + | ckstrdup(const char *s) |
| + | { |
| + | char *p = strdup(s); |
| + | if (!p) |
| + | ash_msg_and_raise_error(bb_msg_memory_exhausted); |
| + | return p; |
| + | } |
| + | /* |
| + | * Parse trees for commands are allocated in lifo order, so we use a stack |
| + | * to make this more efficient, and also to avoid all sorts of exception |
| + | * handling code to handle interrupts in the middle of a parse. |
| + | * |
| + | * The size 504 was chosen because the Ultrix malloc handles that size |
| + | * well. |
| + | */ |
| + | static void * |
| + | stalloc(size_t nbytes) |
| + | { |
| + | char *p; |
| + | size_t aligned; |
| - | union node; |
| - | static void expandarg(union node *, struct arglist *, int); |
| - | #define rmescapes(p) _rmescapes((p), 0) |
| - | static char *_rmescapes(char *, int); |
| - | static int casematch(union node *, char *); |
| + | aligned = SHELL_ALIGN(nbytes); |
| + | if (aligned > stacknleft) { |
| + | size_t len; |
| + | size_t blocksize; |
| + | struct stack_block *sp; |
| - | #if ENABLE_ASH_MATH_SUPPORT |
| - | static void expari(int); |
| - | #endif |
| + | blocksize = aligned; |
| + | if (blocksize < MINSIZE) |
| + | blocksize = MINSIZE; |
| + | len = sizeof(struct stack_block) - MINSIZE + blocksize; |
| + | if (len < blocksize) |
| + | ash_msg_and_raise_error(bb_msg_memory_exhausted); |
| + | INT_OFF; |
| + | sp = ckmalloc(len); |
| + | sp->prev = stackp; |
| + | stacknxt = sp->space; |
| + | stacknleft = blocksize; |
| + | sstrend = stacknxt + blocksize; |
| + | stackp = sp; |
| + | INT_ON; |
| + | } |
| + | p = stacknxt; |
| + | stacknxt += aligned; |
| + | stacknleft -= aligned; |
| + | return p; |
| + | } |
| - | /* eval.h */ |
| + | static void |
| + | stunalloc(void *p) |
| + | { |
| + | #if DEBUG |
| + | if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) { |
| + | write(2, "stunalloc\n", 10); |
| + | abort(); |
| + | } |
| + | #endif |
| + | stacknleft += stacknxt - (char *)p; |
| + | stacknxt = p; |
| + | } |
| + | static void |
| + | setstackmark(struct stackmark *mark) |
| + | { |
| + | mark->stackp = stackp; |
| + | mark->stacknxt = stacknxt; |
| + | mark->stacknleft = stacknleft; |
| + | mark->marknext = markp; |
| + | markp = mark; |
| + | } |
| + | static void |
| + | popstackmark(struct stackmark *mark) |
| + | { |
| + | struct stack_block *sp; |
| - | struct backcmd { /* result of evalbackcmd */ |
| - | int fd; /* file descriptor to read from */ |
| - | char *buf; /* buffer */ |
| - | int nleft; /* number of chars in buffer */ |
| - | struct job *jp; /* job structure for command */ |
| - | }; |
| + | INT_OFF; |
| + | markp = mark->marknext; |
| + | while (stackp != mark->stackp) { |
| + | sp = stackp; |
| + | stackp = sp->prev; |
| + | free(sp); |
| + | } |
| + | stacknxt = mark->stacknxt; |
| + | stacknleft = mark->stacknleft; |
| + | sstrend = mark->stacknxt + mark->stacknleft; |
| + | INT_ON; |
| + | } |
| /* | |
| - | * This file was generated by the mknodes program. |
| + | * When the parser reads in a string, it wants to stick the string on the |
| + | * stack and only adjust the stack pointer when it knows how big the |
| + | * string is. Stackblock (defined in stack.h) returns a pointer to a block |
| + | * of space on top of the stack and stackblocklen returns the length of |
| + | * this block. Growstackblock will grow this space by at least one byte, |
| + | * possibly moving it (like realloc). Grabstackblock actually allocates the |
| + | * part of the block that has been used. |
| */ | |
| + | static void |
| + | growstackblock(void) |
| + | { |
| + | size_t newlen; |
| - | #define NCMD 0 |
| - | #define NPIPE 1 |
| - | #define NREDIR 2 |
| - | #define NBACKGND 3 |
| - | #define NSUBSHELL 4 |
| - | #define NAND 5 |
| - | #define NOR 6 |
| - | #define NSEMI 7 |
| - | #define NIF 8 |
| - | #define NWHILE 9 |
| - | #define NUNTIL 10 |
| - | #define NFOR 11 |
| - | #define NCASE 12 |
| - | #define NCLIST 13 |
| - | #define NDEFUN 14 |
| - | #define NARG 15 |
| - | #define NTO 16 |
| - | #define NCLOBBER 17 |
| - | #define NFROM 18 |
| - | #define NFROMTO 19 |
| - | #define NAPPEND 20 |
| - | #define NTOFD 21 |
| - | #define NFROMFD 22 |
| - | #define NHERE 23 |
| - | #define NXHERE 24 |
| - | #define NNOT 25 |
| + | newlen = stacknleft * 2; |
| + | if (newlen < stacknleft) |
| + | ash_msg_and_raise_error(bb_msg_memory_exhausted); |
| + | if (newlen < 128) |
| + | newlen += 128; |
| + | if (stacknxt == stackp->space && stackp != &stackbase) { |
| + | struct stack_block *oldstackp; |
| + | struct stackmark *xmark; |
| + | struct stack_block *sp; |
| + | struct stack_block *prevstackp; |
| + | size_t grosslen; |
| - | struct ncmd { |
| - | int type; |
| - | union node *assign; |
| - | union node *args; |
| - | union node *redirect; |
| - | }; |
| + | INT_OFF; |
| + | oldstackp = stackp; |
| + | sp = stackp; |
| + | prevstackp = sp->prev; |
| + | grosslen = newlen + sizeof(struct stack_block) - MINSIZE; |
| + | sp = ckrealloc(sp, grosslen); |
| + | sp->prev = prevstackp; |
| + | stackp = sp; |
| + | stacknxt = sp->space; |
| + | stacknleft = newlen; |
| + | sstrend = sp->space + newlen; |
| - | struct npipe { |
| - | int type; |
| - | int backgnd; |
| - | struct nodelist *cmdlist; |
| - | }; |
| + | /* |
| + | * Stack marks pointing to the start of the old block |
| + | * must be relocated to point to the new block |
| + | */ |
| + | xmark = markp; |
| + | while (xmark != NULL && xmark->stackp == oldstackp) { |
| + | xmark->stackp = stackp; |
| + | xmark->stacknxt = stacknxt; |
| + | xmark->stacknleft = stacknleft; |
| + | xmark = xmark->marknext; |
| + | } |
| + | INT_ON; |
| + | } else { |
| + | char *oldspace = stacknxt; |
| + | int oldlen = stacknleft; |
| + | char *p = stalloc(newlen); |
| - | struct nredir { |
| - | int type; |
| - | union node *n; |
| - | union node *redirect; |
| - | }; |
| + | /* free the space we just allocated */ |
| + | stacknxt = memcpy(p, oldspace, oldlen); |
| + | stacknleft += newlen; |
| + | } |
| + | } |
| + | |
| + | static void |
| + | grabstackblock(size_t len) |
| + | { |
| + | len = SHELL_ALIGN(len); |
| + | stacknxt += len; |
| + | stacknleft -= len; |
| + | } |
| + | |
| + | /* |
| + | * The following routines are somewhat easier to use than the above. |
| + | * The user declares a variable of type STACKSTR, which may be declared |
| + | * to be a register. The macro STARTSTACKSTR initializes things. Then |
| + | * the user uses the macro STPUTC to add characters to the string. In |
| + | * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is |
| + | * grown as necessary. When the user is done, she can just leave the |
| + | * string there and refer to it using stackblock(). Or she can allocate |
| + | * the space for it using grabstackstr(). If it is necessary to allow |
| + | * someone else to use the stack temporarily and then continue to grow |
| + | * the string, the user should use grabstack to allocate the space, and |
| + | * then call ungrabstr(p) to return to the previous mode of operation. |
| + | * |
| + | * USTPUTC is like STPUTC except that it doesn't check for overflow. |
| + | * CHECKSTACKSPACE can be called before USTPUTC to ensure that there |
| + | * is space for at least one character. |
| + | */ |
| + | static void * |
| + | growstackstr(void) |
| + | { |
| + | size_t len = stackblocksize(); |
| + | if (herefd >= 0 && len >= 1024) { |
| + | full_write(herefd, stackblock(), len); |
| + | return stackblock(); |
| + | } |
| + | growstackblock(); |
| + | return stackblock() + len; |
| + | } |
| + | |
| + | /* |
| + | * Called from CHECKSTRSPACE. |
| + | */ |
| + | static char * |
| + | makestrspace(size_t newlen, char *p) |
| + | { |
| + | size_t len = p - stacknxt; |
| + | size_t size = stackblocksize(); |
| + | |
| + | for (;;) { |
| + | size_t nleft; |
| + | |
| + | size = stackblocksize(); |
| + | nleft = size - len; |
| + | if (nleft >= newlen) |
| + | break; |
| + | growstackblock(); |
| + | } |
| + | return stackblock() + len; |
| + | } |
| + | |
| + | static char * |
| + | stack_nputstr(const char *s, size_t n, char *p) |
| + | { |
| + | p = makestrspace(n, p); |
| + | p = memcpy(p, s, n) + n; |
| + | return p; |
| + | } |
| + | |
| + | static char * |
| + | stack_putstr(const char *s, char *p) |
| + | { |
| + | return stack_nputstr(s, strlen(s), p); |
| + | } |
| + | |
| + | |
| + | /* ============ Unsorted yet */ |
| + | |
| + | |
| + | static void setpwd(const char *, int); |
| + | |
| + | /* expand.h */ |
| + | |
| + | struct strlist { |
| + | struct strlist *next; |
| + | char *text; |
| + | }; |
| + | |
| + | |
| + | struct arglist { |
| + | struct strlist *list; |
| + | struct strlist **lastp; |
| + | }; |
| + | |
| + | /* |
| + | * expandarg() flags |
| + | */ |
| + | #define EXP_FULL 0x1 /* perform word splitting & file globbing */ |
| + | #define EXP_TILDE 0x2 /* do normal tilde expansion */ |
| + | #define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ |
| + | #define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ |
| + | #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ |
| + | #define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */ |
| + | #define EXP_VARTILDE2 0x40 /* expand tildes after colons only */ |
| + | #define EXP_WORD 0x80 /* expand word in parameter expansion */ |
| + | #define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */ |
| + | |
| + | |
| + | union node; |
| + | static void expandarg(union node *, struct arglist *, int); |
| + | #define rmescapes(p) _rmescapes((p), 0) |
| + | static char *_rmescapes(char *, int); |
| + | static int casematch(union node *, char *); |
| + | |
| + | #if ENABLE_ASH_MATH_SUPPORT |
| + | static void expari(int); |
| + | #endif |
| + | |
| + | /* eval.h */ |
| + | |
| + | |
| + | |
| + | struct backcmd { /* result of evalbackcmd */ |
| + | int fd; /* file descriptor to read from */ |
| + | char *buf; /* buffer */ |
| + | int nleft; /* number of chars in buffer */ |
| + | struct job *jp; /* job structure for command */ |
| + | }; |
| + | |
| + | /* |
| + | * This file was generated by the mknodes program. |
| + | */ |
| + | |
| + | #define NCMD 0 |
| + | #define NPIPE 1 |
| + | #define NREDIR 2 |
| + | #define NBACKGND 3 |
| + | #define NSUBSHELL 4 |
| + | #define NAND 5 |
| + | #define NOR 6 |
| + | #define NSEMI 7 |
| + | #define NIF 8 |
| + | #define NWHILE 9 |
| + | #define NUNTIL 10 |
| + | #define NFOR 11 |
| + | #define NCASE 12 |
| + | #define NCLIST 13 |
| + | #define NDEFUN 14 |
| + | #define NARG 15 |
| + | #define NTO 16 |
| + | #define NCLOBBER 17 |
| + | #define NFROM 18 |
| + | #define NFROMTO 19 |
| + | #define NAPPEND 20 |
| + | #define NTOFD 21 |
| + | #define NFROMFD 22 |
| + | #define NHERE 23 |
| + | #define NXHERE 24 |
| + | #define NNOT 25 |
| + | |
| + | |
| + | struct ncmd { |
| + | int type; |
| + | union node *assign; |
| + | union node *args; |
| + | union node *redirect; |
| + | }; |
| + | |
| + | struct npipe { |
| + | int type; |
| + | int backgnd; |
| + | struct nodelist *cmdlist; |
| + | }; |
| + | |
| + | struct nredir { |
| + | int type; |
| + | union node *n; |
| + | union node *redirect; |
| + | }; |
| struct nbinary { | |
| int type; | |
| @@ -773,9 +1056,7 @@ static union node *redirnode; | |
| static struct heredoc *heredoc; | |
| static int quoteflag; /* set if (part of) last token was quoted */ | |
| - | static union node *parsecmd(int); |
| static void fixredir(union node *, const char *, int); | |
| - | static const char *const *findkwd(const char *); |
| static char *endofname(const char *); | |
| /* shell.h */ | |
| @@ -867,7 +1148,8 @@ static const char *const tokname_array[] = { | |
| "\1}", | |
| }; | |
| - | static const char *tokname(int tok) |
| + | static const char * |
| + | tokname(int tok) |
| { | |
| static char buf[16]; | |
| @@ -878,24 +1160,20 @@ static const char *tokname(int tok) | |
| return buf; | |
| } | |
| - | /* machdep.h */ |
| - | |
| - | /* |
| - | * Most machines require the value returned from malloc to be aligned |
| - | * in some way. The following macro will get this right on many machines. |
| - | */ |
| - | |
| - | #define SHELL_SIZE (sizeof(union {int i; char *cp; double d; }) - 1) |
| - | /* |
| - | * It appears that grabstackstr() will barf with such alignments |
| - | * because stalloc() will return a string allocated in a new stackblock. |
| - | */ |
| - | #define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE) |
| - | |
| - | /* |
| - | * This file was generated by the mksyntax program. |
| - | */ |
| + | /* Wrapper around strcmp for qsort/bsearch/... */ |
| + | static int |
| + | pstrcmp(const void *a, const void *b) |
| + | { |
| + | return strcmp((const char *) a, (*(const char *const *) b) + 1); |
| + | } |
| + | static const char *const * |
| + | findkwd(const char *s) |
| + | { |
| + | return bsearch(s, tokname_array + KWDOFFSET, |
| + | (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET, |
| + | sizeof(const char *), pstrcmp); |
| + | } |
| /* Syntax classes */ | |
| #define CWORD 0 /* character is nothing special */ | |
| @@ -1006,7 +1284,8 @@ static const char S_I_T[][3] = { | |
| #define U_C(c) ((unsigned char)(c)) | |
| - | static int SIT(int c, int syntax) |
| + | static int |
| + | SIT(int c, int syntax) |
| { | |
| static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~"; | |
| #if ENABLE_ASH_ALIAS | |
| @@ -1392,13 +1671,8 @@ static void calcsize(union node *); | |
| static void sizenodelist(struct nodelist *); | |
| static union node *copynode(union node *); | |
| static struct nodelist *copynodelist(struct nodelist *); | |
| - | static char *nodesavestr(char *); |
| - | |
| + | static char *nodeckstrdup(char *); |
| - | static int evalstring(char *, int mask); |
| - | union node; /* BLETCH for ansi C */ |
| - | static void evaltree(union node *, int); |
| - | static void evalbackcmd(union node *, struct backcmd *); |
| static int evalskip; /* set if we are skipping commands */ | |
| static int skipcount; /* number of levels to skip */ | |
| @@ -1637,16 +1911,16 @@ static void change_random(const char *); | |
| # endif | |
| #endif | |
| - | /* init.h */ |
| - | |
| - | static void reset(void); |
| - | |
| /* var.h */ | |
| /* | |
| * Shell variables. | |
| */ | |
| + | #if ENABLE_ASH_GETOPTS |
| + | static void getoptsreset(const char *); |
| + | #endif |
| + | |
| /* flags */ | |
| #define VEXPORT 0x01 /* variable is exported */ | |
| #define VREADONLY 0x02 /* variable cannot be modified */ | |
| @@ -1684,11 +1958,6 @@ static struct localvar *localvars; | |
| /* | |
| * Shell variables. | |
| */ | |
| - | |
| - | #if ENABLE_ASH_GETOPTS |
| - | static void getoptsreset(const char *); |
| - | #endif |
| - | |
| #if ENABLE_LOCALE_SUPPORT | |
| static void change_lc_all(const char *value); | |
| static void change_lc_ctype(const char *value); | |
| @@ -1950,46 +2219,6 @@ static void showjobs(FILE *, int); | |
| static void readcmdfile(char *); | |
| - | /* memalloc.h */ |
| - | |
| - | |
| - | struct stackmark { |
| - | struct stack_block *stackp; |
| - | char *stacknxt; |
| - | size_t stacknleft; |
| - | struct stackmark *marknext; |
| - | }; |
| - | |
| - | /* minimum size of a block */ |
| - | #define MINSIZE SHELL_ALIGN(504) |
| - | |
| - | struct stack_block { |
| - | struct stack_block *prev; |
| - | char space[MINSIZE]; |
| - | }; |
| - | |
| - | static struct stack_block stackbase; |
| - | static struct stack_block *stackp = &stackbase; |
| - | static struct stackmark *markp; |
| - | static char *stacknxt = stackbase.space; |
| - | static size_t stacknleft = MINSIZE; |
| - | static char *sstrend = stackbase.space + MINSIZE; |
| - | static int herefd = -1; |
| - | |
| - | |
| - | static void *ckmalloc(size_t); |
| - | static void *ckrealloc(void *, size_t); |
| - | static char *savestr(const char *); |
| - | static void *stalloc(size_t); |
| - | static void stunalloc(void *); |
| - | static void setstackmark(struct stackmark *); |
| - | static void popstackmark(struct stackmark *); |
| - | static void growstackblock(void); |
| - | static void *growstackstr(void); |
| - | static char *makestrspace(size_t, char *); |
| - | static char *stnputs(const char *, size_t, char *); |
| - | static char *stputs(const char *, char *); |
| - | |
| static char *_STPUTC(int c, char *p) | |
| { | |
| @@ -1999,8 +2228,6 @@ static char *_STPUTC(int c, char *p) | |
| return p; | |
| } | |
| - | #define stackblock() ((void *)stacknxt) |
| - | #define stackblocksize() stacknleft |
| #define STARTSTACKSTR(p) ((p) = stackblock()) | |
| #define STPUTC(c, p) ((p) = _STPUTC((c), (p))) | |
| #define CHECKSTRSPACE(n, p) \ | |
| @@ -2070,7 +2297,6 @@ static int nextopt(const char *); | |
| #define REDIR_PUSH 01 /* save previous values of file descriptors */ | |
| #define REDIR_SAVEFD2 03 /* set preverrout */ | |
| - | union node; |
| static void redirect(union node *, int); | |
| static void popredir(int); | |
| static void clearredir(int); | |
| @@ -2137,40 +2363,17 @@ static int is_safe_applet(char *name) | |
| } | |
| - | /* |
| - | * This routine is called when an error or an interrupt occurs in an |
| - | * interactive shell and control is returned to the main command loop. |
| - | */ |
| + | #if ENABLE_ASH_ALIAS |
| + | static struct alias *atab[ATABSIZE]; |
| + | |
| + | static void setalias(const char *, const char *); |
| + | static struct alias *freealias(struct alias *); |
| + | static struct alias **__lookupalias(const char *); |
| + | |
| static void | |
| - | reset(void) |
| + | setalias(const char *name, const char *val) |
| { | |
| - | /* from eval.c: */ |
| - | evalskip = 0; |
| - | loopnest = 0; |
| - | |
| - | /* from input.c: */ |
| - | parselleft = parsenleft = 0; /* clear input buffer */ |
| - | popallfiles(); |
| - | |
| - | /* from parser.c: */ |
| - | tokpushback = 0; |
| - | checkkwd = 0; |
| - | |
| - | /* from redir.c: */ |
| - | clearredir(0); |
| - | } |
| - | |
| - | #if ENABLE_ASH_ALIAS |
| - | static struct alias *atab[ATABSIZE]; |
| - | |
| - | static void setalias(const char *, const char *); |
| - | static struct alias *freealias(struct alias *); |
| - | static struct alias **__lookupalias(const char *); |
| - | |
| - | static void |
| - | setalias(const char *name, const char *val) |
| - | { |
| - | struct alias *ap, **app; |
| + | struct alias *ap, **app; |
| app = __lookupalias(name); | |
| ap = *app; | |
| @@ -2179,13 +2382,13 @@ setalias(const char *name, const char *val) | |
| if (!(ap->flag & ALIASINUSE)) { | |
| free(ap->val); | |
| } | |
| - | ap->val = savestr(val); |
| + | ap->val = ckstrdup(val); |
| ap->flag &= ~ALIASDEAD; | |
| } else { | |
| /* not found */ | |
| ap = ckmalloc(sizeof(struct alias)); | |
| - | ap->name = savestr(name); |
| - | ap->val = savestr(val); |
| + | ap->name = ckstrdup(name); |
| + | ap->val = ckstrdup(val); |
| ap->flag = 0; | |
| ap->next = 0; | |
| *app = ap; | |
| @@ -2461,7 +2664,7 @@ static const char * updatepwd(const char *dir) | |
| if (*dir != '/') { | |
| if (curdir == nullstr) | |
| return 0; | |
| - | new = stputs(curdir, new); |
| + | new = stack_putstr(curdir, new); |
| } | |
| new = makestrspace(strlen(dir) + 2, new); | |
| lim = stackblock() + 1; | |
| @@ -2494,7 +2697,7 @@ static const char * updatepwd(const char *dir) | |
| break; | |
| /* fall through */ | |
| default: | |
| - | new = stputs(p, new); |
| + | new = stack_putstr(p, new); |
| USTPUTC('/', new); | |
| } | |
| p = strtok(0, "/"); | |
| @@ -2582,7 +2785,7 @@ setpwd(const char *val, int setold) | |
| if (!val) | |
| dir = s; | |
| } else | |
| - | dir = savestr(val); |
| + | dir = ckstrdup(val); |
| if (oldcur != dir && oldcur != nullstr) { | |
| free(oldcur); | |
| } | |
| @@ -2621,70 +2824,6 @@ static const struct builtincmd bltin = { | |
| }; | |
| - | /* |
| - | * Called to reset things after an exception. |
| - | */ |
| - | |
| - | /* |
| - | * The eval command. |
| - | */ |
| - | static int |
| - | evalcmd(int argc, char **argv) |
| - | { |
| - | char *p; |
| - | char *concat; |
| - | char **ap; |
| - | |
| - | if (argc > 1) { |
| - | p = argv[1]; |
| - | if (argc > 2) { |
| - | STARTSTACKSTR(concat); |
| - | ap = argv + 2; |
| - | for (;;) { |
| - | concat = stputs(p, concat); |
| - | p = *ap++; |
| - | if (p == NULL) |
| - | break; |
| - | STPUTC(' ', concat); |
| - | } |
| - | STPUTC('\0', concat); |
| - | p = grabstackstr(concat); |
| - | } |
| - | evalstring(p, ~SKIPEVAL); |
| - | |
| - | } |
| - | return exitstatus; |
| - | } |
| - | |
| - | |
| - | /* |
| - | * Execute a command or commands contained in a string. |
| - | */ |
| - | static int |
| - | evalstring(char *s, int mask) |
| - | { |
| - | union node *n; |
| - | struct stackmark smark; |
| - | int skip; |
| - | |
| - | setinputstring(s); |
| - | setstackmark(&smark); |
| - | |
| - | skip = 0; |
| - | while ((n = parsecmd(0)) != NEOF) { |
| - | evaltree(n, 0); |
| - | popstackmark(&smark); |
| - | skip = evalskip; |
| - | if (skip) |
| - | break; |
| - | } |
| - | popfile(); |
| - | |
| - | skip &= mask; |
| - | evalskip = skip; |
| - | return skip; |
| - | } |
| - | |
| /* | |
| * Evaluate a parse tree. The value is left in the global variable | |
| @@ -3992,15 +4131,6 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |
| } | |
| - | /* |
| - | * Wrapper around strcmp for qsort/bsearch/... |
| - | */ |
| - | static int pstrcmp(const void *a, const void *b) |
| - | { |
| - | return strcmp((const char *) a, (*(const char *const *) b) + 1); |
| - | } |
| - | |
| - | |
| /* | |
| * Search the table of builtin commands. | |
| */ | |
| @@ -4606,7 +4736,7 @@ argstr(char *p, int flag) | |
| } | |
| if (length > 0) { | |
| int newloc; | |
| - | expdest = stnputs(p, length, expdest); |
| + | expdest = stack_nputstr(p, length, expdest); |
| newloc = expdest - (char *)stackblock(); | |
| if (breakall && !inquotes && newloc > startloc) { | |
| recordregion(startloc, newloc, 0); | |
| @@ -7370,7 +7500,7 @@ commandtext(union node *n) | |
| name = stackblock(); | |
| TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n", | |
| name, cmdnextc, cmdnextc)); | |
| - | return savestr(name); |
| + | return ckstrdup(name); |
| } | |
| static void | |
| @@ -7579,592 +7709,208 @@ cmdputs(const char *s) | |
| str = "$(...)"; | |
| goto dostr; | |
| case CTLBACKQ+CTLQUOTE: | |
| - | str = "\"$(...)\""; |
| - | goto dostr; |
| - | #if ENABLE_ASH_MATH_SUPPORT |
| - | case CTLARI: |
| - | str = "$(("; |
| - | goto dostr; |
| - | case CTLENDARI: |
| - | str = "))"; |
| - | goto dostr; |
| - | #endif |
| - | case CTLQUOTEMARK: |
| - | quoted ^= 1; |
| - | c = '"'; |
| - | break; |
| - | case '=': |
| - | if (subtype == 0) |
| - | break; |
| - | if ((subtype & VSTYPE) != VSNORMAL) |
| - | quoted <<= 1; |
| - | str = vstype[subtype & VSTYPE]; |
| - | if (subtype & VSNUL) |
| - | c = ':'; |
| - | else |
| - | goto checkstr; |
| - | break; |
| - | case '\'': |
| - | case '\\': |
| - | case '"': |
| - | case '$': |
| - | /* These can only happen inside quotes */ |
| - | cc[0] = c; |
| - | str = cc; |
| - | c = '\\'; |
| - | break; |
| - | default: |
| - | break; |
| - | } |
| - | USTPUTC(c, nextc); |
| - | checkstr: |
| - | if (!str) |
| - | continue; |
| - | dostr: |
| - | while ((c = *str++)) { |
| - | USTPUTC(c, nextc); |
| - | } |
| - | } |
| - | if (quoted & 1) { |
| - | USTPUTC('"', nextc); |
| - | } |
| - | *nextc = 0; |
| - | cmdnextc = nextc; |
| - | } |
| - | |
| - | |
| - | static void |
| - | showpipe(struct job *jp, FILE *out) |
| - | { |
| - | struct procstat *sp; |
| - | struct procstat *spend; |
| - | |
| - | spend = jp->ps + jp->nprocs; |
| - | for (sp = jp->ps + 1; sp < spend; sp++) |
| - | fprintf(out, " | %s", sp->cmd); |
| - | outcslow('\n', out); |
| - | flush_stdout_stderr(); |
| - | } |
| - | |
| - | static void |
| - | xtcsetpgrp(int fd, pid_t pgrp) |
| - | { |
| - | if (tcsetpgrp(fd, pgrp)) |
| - | ash_msg_and_raise_error("Cannot set tty process group (%m)"); |
| - | } |
| - | #endif /* JOBS */ |
| - | |
| - | static int |
| - | getstatus(struct job *job) |
| - | { |
| - | int status; |
| - | int retval; |
| - | |
| - | status = job->ps[job->nprocs - 1].status; |
| - | retval = WEXITSTATUS(status); |
| - | if (!WIFEXITED(status)) { |
| - | #if JOBS |
| - | retval = WSTOPSIG(status); |
| - | if (!WIFSTOPPED(status)) |
| - | #endif |
| - | { |
| - | /* XXX: limits number of signals */ |
| - | retval = WTERMSIG(status); |
| - | #if JOBS |
| - | if (retval == SIGINT) |
| - | job->sigint = 1; |
| - | #endif |
| - | } |
| - | retval += 128; |
| - | } |
| - | TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n", |
| - | jobno(job), job->nprocs, status, retval)); |
| - | return retval; |
| - | } |
| - | |
| - | #if ENABLE_ASH_MAIL |
| - | /* mail.c */ |
| - | |
| - | /* |
| - | * Routines to check for mail. (Perhaps make part of main.c?) |
| - | */ |
| - | |
| - | #define MAXMBOXES 10 |
| - | |
| - | /* times of mailboxes */ |
| - | static time_t mailtime[MAXMBOXES]; |
| - | /* Set if MAIL or MAILPATH is changed. */ |
| - | static int mail_var_path_changed; |
| - | |
| - | |
| - | /* |
| - | * Print appropriate message(s) if mail has arrived. |
| - | * If mail_var_path_changed is set, |
| - | * then the value of MAIL has mail_var_path_changed, |
| - | * so we just update the values. |
| - | */ |
| - | static void |
| - | chkmail(void) |
| - | { |
| - | const char *mpath; |
| - | char *p; |
| - | char *q; |
| - | time_t *mtp; |
| - | struct stackmark smark; |
| - | struct stat statb; |
| - | |
| - | setstackmark(&smark); |
| - | mpath = mpathset() ? mpathval() : mailval(); |
| - | for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) { |
| - | p = padvance(&mpath, nullstr); |
| - | if (p == NULL) |
| - | break; |
| - | if (*p == '\0') |
| - | continue; |
| - | for (q = p; *q; q++); |
| - | #if DEBUG |
| - | if (q[-1] != '/') |
| - | abort(); |
| - | #endif |
| - | q[-1] = '\0'; /* delete trailing '/' */ |
| - | if (stat(p, &statb) < 0) { |
| - | *mtp = 0; |
| - | continue; |
| - | } |
| - | if (!mail_var_path_changed && statb.st_mtime != *mtp) { |
| - | fprintf( |
| - | stderr, snlfmt, |
| - | pathopt ? pathopt : "you have mail" |
| - | ); |
| - | } |
| - | *mtp = statb.st_mtime; |
| - | } |
| - | mail_var_path_changed = 0; |
| - | popstackmark(&smark); |
| - | } |
| - | |
| - | |
| - | static void |
| - | changemail(const char *val) |
| - | { |
| - | mail_var_path_changed++; |
| - | } |
| - | |
| - | #endif /* ASH_MAIL */ |
| - | |
| - | /* |
| - | * Read and execute commands. "Top" is nonzero for the top level command |
| - | * loop; it turns on prompting if the shell is interactive. |
| - | */ |
| - | static int |
| - | cmdloop(int top) |
| - | { |
| - | union node *n; |
| - | struct stackmark smark; |
| - | int inter; |
| - | int numeof = 0; |
| - | |
| - | TRACE(("cmdloop(%d) called\n", top)); |
| - | for (;;) { |
| - | int skip; |
| - | |
| - | setstackmark(&smark); |
| - | #if JOBS |
| - | if (jobctl) |
| - | showjobs(stderr, SHOW_CHANGED); |
| - | #endif |
| - | inter = 0; |
| - | if (iflag && top) { |
| - | inter++; |
| - | #if ENABLE_ASH_MAIL |
| - | chkmail(); |
| - | #endif |
| - | } |
| - | n = parsecmd(inter); |
| - | /* showtree(n); DEBUG */ |
| - | if (n == NEOF) { |
| - | if (!top || numeof >= 50) |
| - | break; |
| - | if (!stoppedjobs()) { |
| - | if (!Iflag) |
| - | break; |
| - | out2str("\nUse \"exit\" to leave shell.\n"); |
| - | } |
| - | numeof++; |
| - | } else if (nflag == 0) { |
| - | job_warning = (job_warning == 2) ? 1 : 0; |
| - | numeof = 0; |
| - | evaltree(n, 0); |
| - | } |
| - | popstackmark(&smark); |
| - | skip = evalskip; |
| - | |
| - | if (skip) { |
| - | evalskip = 0; |
| - | return skip & SKIPEVAL; |
| - | } |
| - | } |
| - | |
| - | return 0; |
| - | } |
| - | |
| - | |
| - | /* |
| - | * Read a file containing shell functions. |
| - | */ |
| - | static void |
| - | readcmdfile(char *name) |
| - | { |
| - | setinputfile(name, INPUT_PUSH_FILE); |
| - | cmdloop(0); |
| - | popfile(); |
| - | } |
| - | |
| - | |
| - | /* |
| - | * Take commands from a file. To be compatible we should do a path |
| - | * search for the file, which is necessary to find sub-commands. |
| - | */ |
| - | static char * find_dot_file(char *name) |
| - | { |
| - | char *fullname; |
| - | const char *path = pathval(); |
| - | struct stat statb; |
| - | |
| - | /* don't try this for absolute or relative paths */ |
| - | if (strchr(name, '/')) |
| - | return name; |
| - | |
| - | while ((fullname = padvance(&path, name)) != NULL) { |
| - | if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { |
| - | /* |
| - | * Don't bother freeing here, since it will |
| - | * be freed by the caller. |
| - | */ |
| - | return fullname; |
| - | } |
| - | stunalloc(fullname); |
| - | } |
| - | |
| - | /* not found in the PATH */ |
| - | ash_msg_and_raise_error("%s: not found", name); |
| - | /* NOTREACHED */ |
| - | } |
| - | |
| - | static int dotcmd(int argc, char **argv) |
| - | { |
| - | struct strlist *sp; |
| - | volatile struct shparam saveparam; |
| - | int status = 0; |
| - | |
| - | for (sp = cmdenviron; sp; sp = sp->next) |
| - | setvareq(xstrdup(sp->text), VSTRFIXED | VTEXTFIXED); |
| - | |
| - | if (argc >= 2) { /* That's what SVR2 does */ |
| - | char *fullname; |
| - | |
| - | fullname = find_dot_file(argv[1]); |
| - | |
| - | if (argc > 2) { |
| - | saveparam = shellparam; |
| - | shellparam.malloc = 0; |
| - | shellparam.nparam = argc - 2; |
| - | shellparam.p = argv + 2; |
| - | }; |
| - | |
| - | setinputfile(fullname, INPUT_PUSH_FILE); |
| - | commandname = fullname; |
| - | cmdloop(0); |
| - | popfile(); |
| - | |
| - | if (argc > 2) { |
| - | freeparam(&shellparam); |
| - | shellparam = saveparam; |
| - | }; |
| - | status = exitstatus; |
| - | } |
| - | return status; |
| - | } |
| - | |
| - | |
| - | static int |
| - | exitcmd(int argc, char **argv) |
| - | { |
| - | if (stoppedjobs()) |
| - | return 0; |
| - | if (argc > 1) |
| - | exitstatus = number(argv[1]); |
| - | raise_exception(EXEXIT); |
| - | /* NOTREACHED */ |
| - | } |
| - | |
| - | #if ENABLE_ASH_BUILTIN_ECHO |
| - | static int |
| - | echocmd(int argc, char **argv) |
| - | { |
| - | return bb_echo(argv); |
| - | } |
| - | #endif |
| - | |
| - | #if ENABLE_ASH_BUILTIN_TEST |
| - | static int |
| - | testcmd(int argc, char **argv) |
| - | { |
| - | return bb_test(argc, argv); |
| - | } |
| - | #endif |
| - | |
| - | /* memalloc.c */ |
| - | |
| - | /* |
| - | * Same for malloc, realloc, but returns an error when out of space. |
| - | */ |
| - | static void * |
| - | ckrealloc(void * p, size_t nbytes) |
| - | { |
| - | p = realloc(p, nbytes); |
| - | if (p == NULL) |
| - | ash_msg_and_raise_error(bb_msg_memory_exhausted); |
| - | return p; |
| - | } |
| - | |
| - | static void * |
| - | ckmalloc(size_t nbytes) |
| - | { |
| - | return ckrealloc(NULL, nbytes); |
| - | } |
| - | |
| - | /* |
| - | * Make a copy of a string in safe storage. |
| - | */ |
| - | static char * |
| - | savestr(const char *s) |
| - | { |
| - | char *p = strdup(s); |
| - | if (!p) |
| - | ash_msg_and_raise_error(bb_msg_memory_exhausted); |
| - | return p; |
| - | } |
| - | |
| - | |
| - | /* |
| - | * Parse trees for commands are allocated in lifo order, so we use a stack |
| - | * to make this more efficient, and also to avoid all sorts of exception |
| - | * handling code to handle interrupts in the middle of a parse. |
| - | * |
| - | * The size 504 was chosen because the Ultrix malloc handles that size |
| - | * well. |
| - | */ |
| - | static void * |
| - | stalloc(size_t nbytes) |
| - | { |
| - | char *p; |
| - | size_t aligned; |
| - | |
| - | aligned = SHELL_ALIGN(nbytes); |
| - | if (aligned > stacknleft) { |
| - | size_t len; |
| - | size_t blocksize; |
| - | struct stack_block *sp; |
| - | |
| - | blocksize = aligned; |
| - | if (blocksize < MINSIZE) |
| - | blocksize = MINSIZE; |
| - | len = sizeof(struct stack_block) - MINSIZE + blocksize; |
| - | if (len < blocksize) |
| - | ash_msg_and_raise_error(bb_msg_memory_exhausted); |
| - | INT_OFF; |
| - | sp = ckmalloc(len); |
| - | sp->prev = stackp; |
| - | stacknxt = sp->space; |
| - | stacknleft = blocksize; |
| - | sstrend = stacknxt + blocksize; |
| - | stackp = sp; |
| - | INT_ON; |
| + | str = "\"$(...)\""; |
| + | goto dostr; |
| + | #if ENABLE_ASH_MATH_SUPPORT |
| + | case CTLARI: |
| + | str = "$(("; |
| + | goto dostr; |
| + | case CTLENDARI: |
| + | str = "))"; |
| + | goto dostr; |
| + | #endif |
| + | case CTLQUOTEMARK: |
| + | quoted ^= 1; |
| + | c = '"'; |
| + | break; |
| + | case '=': |
| + | if (subtype == 0) |
| + | break; |
| + | if ((subtype & VSTYPE) != VSNORMAL) |
| + | quoted <<= 1; |
| + | str = vstype[subtype & VSTYPE]; |
| + | if (subtype & VSNUL) |
| + | c = ':'; |
| + | else |
| + | goto checkstr; |
| + | break; |
| + | case '\'': |
| + | case '\\': |
| + | case '"': |
| + | case '$': |
| + | /* These can only happen inside quotes */ |
| + | cc[0] = c; |
| + | str = cc; |
| + | c = '\\'; |
| + | break; |
| + | default: |
| + | break; |
| + | } |
| + | USTPUTC(c, nextc); |
| + | checkstr: |
| + | if (!str) |
| + | continue; |
| + | dostr: |
| + | while ((c = *str++)) { |
| + | USTPUTC(c, nextc); |
| + | } |
| } | |
| - | p = stacknxt; |
| - | stacknxt += aligned; |
| - | stacknleft -= aligned; |
| - | return p; |
| + | if (quoted & 1) { |
| + | USTPUTC('"', nextc); |
| + | } |
| + | *nextc = 0; |
| + | cmdnextc = nextc; |
| } | |
| static void | |
| - | stunalloc(void *p) |
| + | showpipe(struct job *jp, FILE *out) |
| { | |
| - | #if DEBUG |
| - | if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) { |
| - | write(2, "stunalloc\n", 10); |
| - | abort(); |
| - | } |
| - | #endif |
| - | stacknleft += stacknxt - (char *)p; |
| - | stacknxt = p; |
| - | } |
| + | struct procstat *sp; |
| + | struct procstat *spend; |
| + | spend = jp->ps + jp->nprocs; |
| + | for (sp = jp->ps + 1; sp < spend; sp++) |
| + | fprintf(out, " | %s", sp->cmd); |
| + | outcslow('\n', out); |
| + | flush_stdout_stderr(); |
| + | } |
| static void | |
| - | setstackmark(struct stackmark *mark) |
| + | xtcsetpgrp(int fd, pid_t pgrp) |
| { | |
| - | mark->stackp = stackp; |
| - | mark->stacknxt = stacknxt; |
| - | mark->stacknleft = stacknleft; |
| - | mark->marknext = markp; |
| - | markp = mark; |
| + | if (tcsetpgrp(fd, pgrp)) |
| + | ash_msg_and_raise_error("Cannot set tty process group (%m)"); |
| } | |
| + | #endif /* JOBS */ |
| - | |
| - | static void |
| - | popstackmark(struct stackmark *mark) |
| + | static int |
| + | getstatus(struct job *job) |
| { | |
| - | struct stack_block *sp; |
| + | int status; |
| + | int retval; |
| - | INT_OFF; |
| - | markp = mark->marknext; |
| - | while (stackp != mark->stackp) { |
| - | sp = stackp; |
| - | stackp = sp->prev; |
| - | free(sp); |
| + | status = job->ps[job->nprocs - 1].status; |
| + | retval = WEXITSTATUS(status); |
| + | if (!WIFEXITED(status)) { |
| + | #if JOBS |
| + | retval = WSTOPSIG(status); |
| + | if (!WIFSTOPPED(status)) |
| + | #endif |
| + | { |
| + | /* XXX: limits number of signals */ |
| + | retval = WTERMSIG(status); |
| + | #if JOBS |
| + | if (retval == SIGINT) |
| + | job->sigint = 1; |
| + | #endif |
| + | } |
| + | retval += 128; |
| } | |
| - | stacknxt = mark->stacknxt; |
| - | stacknleft = mark->stacknleft; |
| - | sstrend = mark->stacknxt + mark->stacknleft; |
| - | INT_ON; |
| + | TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n", |
| + | jobno(job), job->nprocs, status, retval)); |
| + | return retval; |
| } | |
| + | #if ENABLE_ASH_MAIL |
| + | /* mail.c */ |
| /* | |
| - | * When the parser reads in a string, it wants to stick the string on the |
| - | * stack and only adjust the stack pointer when it knows how big the |
| - | * string is. Stackblock (defined in stack.h) returns a pointer to a block |
| - | * of space on top of the stack and stackblocklen returns the length of |
| - | * this block. Growstackblock will grow this space by at least one byte, |
| - | * possibly moving it (like realloc). Grabstackblock actually allocates the |
| - | * part of the block that has been used. |
| + | * Routines to check for mail. (Perhaps make part of main.c?) |
| */ | |
| - | static void |
| - | growstackblock(void) |
| - | { |
| - | size_t newlen; |
| - | newlen = stacknleft * 2; |
| - | if (newlen < stacknleft) |
| - | ash_msg_and_raise_error(bb_msg_memory_exhausted); |
| - | if (newlen < 128) |
| - | newlen += 128; |
| + | #define MAXMBOXES 10 |
| - | if (stacknxt == stackp->space && stackp != &stackbase) { |
| - | struct stack_block *oldstackp; |
| - | struct stackmark *xmark; |
| - | struct stack_block *sp; |
| - | struct stack_block *prevstackp; |
| - | size_t grosslen; |
| + | /* times of mailboxes */ |
| + | static time_t mailtime[MAXMBOXES]; |
| + | /* Set if MAIL or MAILPATH is changed. */ |
| + | static int mail_var_path_changed; |
| - | INT_OFF; |
| - | oldstackp = stackp; |
| - | sp = stackp; |
| - | prevstackp = sp->prev; |
| - | grosslen = newlen + sizeof(struct stack_block) - MINSIZE; |
| - | sp = ckrealloc(sp, grosslen); |
| - | sp->prev = prevstackp; |
| - | stackp = sp; |
| - | stacknxt = sp->space; |
| - | stacknleft = newlen; |
| - | sstrend = sp->space + newlen; |
| - | /* |
| - | * Stack marks pointing to the start of the old block |
| - | * must be relocated to point to the new block |
| - | */ |
| - | xmark = markp; |
| - | while (xmark != NULL && xmark->stackp == oldstackp) { |
| - | xmark->stackp = stackp; |
| - | xmark->stacknxt = stacknxt; |
| - | xmark->stacknleft = stacknleft; |
| - | xmark = xmark->marknext; |
| - | } |
| - | INT_ON; |
| - | } else { |
| - | char *oldspace = stacknxt; |
| - | int oldlen = stacknleft; |
| - | char *p = stalloc(newlen); |
| + | /* |
| + | * Print appropriate message(s) if mail has arrived. |
| + | * If mail_var_path_changed is set, |
| + | * then the value of MAIL has mail_var_path_changed, |
| + | * so we just update the values. |
| + | */ |
| + | static void |
| + | chkmail(void) |
| + | { |
| + | const char *mpath; |
| + | char *p; |
| + | char *q; |
| + | time_t *mtp; |
| + | struct stackmark smark; |
| + | struct stat statb; |
| - | /* free the space we just allocated */ |
| - | stacknxt = memcpy(p, oldspace, oldlen); |
| - | stacknleft += newlen; |
| + | setstackmark(&smark); |
| + | mpath = mpathset() ? mpathval() : mailval(); |
| + | for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) { |
| + | p = padvance(&mpath, nullstr); |
| + | if (p == NULL) |
| + | break; |
| + | if (*p == '\0') |
| + | continue; |
| + | for (q = p; *q; q++); |
| + | #if DEBUG |
| + | if (q[-1] != '/') |
| + | abort(); |
| + | #endif |
| + | q[-1] = '\0'; /* delete trailing '/' */ |
| + | if (stat(p, &statb) < 0) { |
| + | *mtp = 0; |
| + | continue; |
| + | } |
| + | if (!mail_var_path_changed && statb.st_mtime != *mtp) { |
| + | fprintf( |
| + | stderr, snlfmt, |
| + | pathopt ? pathopt : "you have mail" |
| + | ); |
| + | } |
| + | *mtp = statb.st_mtime; |
| } | |
| - | } |
| - | |
| - | static void grabstackblock(size_t len) |
| - | { |
| - | len = SHELL_ALIGN(len); |
| - | stacknxt += len; |
| - | stacknleft -= len; |
| + | mail_var_path_changed = 0; |
| + | popstackmark(&smark); |
| } | |
| - | /* |
| - | * The following routines are somewhat easier to use than the above. |
| - | * The user declares a variable of type STACKSTR, which may be declared |
| - | * to be a register. The macro STARTSTACKSTR initializes things. Then |
| - | * the user uses the macro STPUTC to add characters to the string. In |
| - | * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is |
| - | * grown as necessary. When the user is done, she can just leave the |
| - | * string there and refer to it using stackblock(). Or she can allocate |
| - | * the space for it using grabstackstr(). If it is necessary to allow |
| - | * someone else to use the stack temporarily and then continue to grow |
| - | * the string, the user should use grabstack to allocate the space, and |
| - | * then call ungrabstr(p) to return to the previous mode of operation. |
| - | * |
| - | * USTPUTC is like STPUTC except that it doesn't check for overflow. |
| - | * CHECKSTACKSPACE can be called before USTPUTC to ensure that there |
| - | * is space for at least one character. |
| - | */ |
| - | static void * |
| - | growstackstr(void) |
| + | static void |
| + | changemail(const char *val) |
| { | |
| - | size_t len = stackblocksize(); |
| - | if (herefd >= 0 && len >= 1024) { |
| - | full_write(herefd, stackblock(), len); |
| - | return stackblock(); |
| - | } |
| - | growstackblock(); |
| - | return stackblock() + len; |
| + | mail_var_path_changed++; |
| } | |
| + | #endif /* ASH_MAIL */ |
| + | |
| /* | |
| - | * Called from CHECKSTRSPACE. |
| + | * Take commands from a file. To be compatible we should do a path |
| + | * search for the file, which is necessary to find sub-commands. |
| */ | |
| static char * | |
| - | makestrspace(size_t newlen, char *p) |
| + | find_dot_file(char *name) |
| { | |
| - | size_t len = p - stacknxt; |
| - | size_t size = stackblocksize(); |
| + | char *fullname; |
| + | const char *path = pathval(); |
| + | struct stat statb; |
| - | for (;;) { |
| - | size_t nleft; |
| + | /* don't try this for absolute or relative paths */ |
| + | if (strchr(name, '/')) |
| + | return name; |
| - | size = stackblocksize(); |
| - | nleft = size - len; |
| - | if (nleft >= newlen) |
| - | break; |
| - | growstackblock(); |
| + | while ((fullname = padvance(&path, name)) != NULL) { |
| + | if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { |
| + | /* |
| + | * Don't bother freeing here, since it will |
| + | * be freed by the caller. |
| + | */ |
| + | return fullname; |
| + | } |
| + | stunalloc(fullname); |
| } | |
| - | return stackblock() + len; |
| - | } |
| - | |
| - | static char * |
| - | stnputs(const char *s, size_t n, char *p) |
| - | { |
| - | p = makestrspace(n, p); |
| - | p = memcpy(p, s, n) + n; |
| - | return p; |
| - | } |
| - | static char * |
| - | stputs(const char *s, char *p) |
| - | { |
| - | return stnputs(s, strlen(s), p); |
| + | /* not found in the PATH */ |
| + | ash_msg_and_raise_error("%s: not found", name); |
| + | /* NOTREACHED */ |
| } | |
| /* mystring.c */ | |
| @@ -8404,7 +8150,7 @@ copynode(union node *n) | |
| new->nif.test = copynode(n->nif.test); | |
| break; | |
| case NFOR: | |
| - | new->nfor.var = nodesavestr(n->nfor.var); |
| + | new->nfor.var = nodeckstrdup(n->nfor.var); |
| new->nfor.body = copynode(n->nfor.body); | |
| new->nfor.args = copynode(n->nfor.args); | |
| break; | |
| @@ -8420,7 +8166,7 @@ copynode(union node *n) | |
| case NDEFUN: | |
| case NARG: | |
| new->narg.backquote = copynodelist(n->narg.backquote); | |
| - | new->narg.text = nodesavestr(n->narg.text); |
| + | new->narg.text = nodeckstrdup(n->narg.text); |
| new->narg.next = copynode(n->narg.next); | |
| break; | |
| case NTO: | |
| @@ -8474,7 +8220,7 @@ copynodelist(struct nodelist *lp) | |
| static char * | |
| - | nodesavestr(char *s) |
| + | nodeckstrdup(char *s) |
| { | |
| char *rtn = funcstring; | |
| @@ -8612,7 +8358,7 @@ setparam(char **argv) | |
| for (nparam = 0; argv[nparam]; nparam++); | |
| ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap)); | |
| while (*argv) { | |
| - | *ap++ = savestr(*argv++); |
| + | *ap++ = ckstrdup(*argv++); |
| } | |
| *ap = NULL; | |
| freeparam(&shellparam); | |
| @@ -9411,7 +9157,8 @@ makename(void) | |
| return n; | |
| } | |
| - | static void fixredir(union node *n, const char *text, int err) |
| + | static void |
| + | fixredir(union node *n, const char *text, int err) |
| { | |
| TRACE(("Fix redir %s %d\n", text, err)); | |
| if (!err) | |
| @@ -9491,7 +9238,8 @@ parseheredoc(void) | |
| } | |
| } | |
| - | static char peektoken(void) |
| + | static char |
| + | peektoken(void) |
| { | |
| int t; | |
| @@ -10453,13 +10201,195 @@ static void setprompt(int whichprompt) | |
| } | |
| - | static const char *const *findkwd(const char *s) |
| + | /* |
| + | * Execute a command or commands contained in a string. |
| + | */ |
| + | static int |
| + | evalstring(char *s, int mask) |
| { | |
| - | return bsearch(s, tokname_array + KWDOFFSET, |
| - | (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET, |
| - | sizeof(const char *), pstrcmp); |
| + | union node *n; |
| + | struct stackmark smark; |
| + | int skip; |
| + | |
| + | setinputstring(s); |
| + | setstackmark(&smark); |
| + | |
| + | skip = 0; |
| + | while ((n = parsecmd(0)) != NEOF) { |
| + | evaltree(n, 0); |
| + | popstackmark(&smark); |
| + | skip = evalskip; |
| + | if (skip) |
| + | break; |
| + | } |
| + | popfile(); |
| + | |
| + | skip &= mask; |
| + | evalskip = skip; |
| + | return skip; |
| + | } |
| + | |
| + | /* |
| + | * The eval command. |
| + | */ |
| + | static int |
| + | evalcmd(int argc, char **argv) |
| + | { |
| + | char *p; |
| + | char *concat; |
| + | char **ap; |
| + | |
| + | if (argc > 1) { |
| + | p = argv[1]; |
| + | if (argc > 2) { |
| + | STARTSTACKSTR(concat); |
| + | ap = argv + 2; |
| + | for (;;) { |
| + | concat = stack_putstr(p, concat); |
| + | p = *ap++; |
| + | if (p == NULL) |
| + | break; |
| + | STPUTC(' ', concat); |
| + | } |
| + | STPUTC('\0', concat); |
| + | p = grabstackstr(concat); |
| + | } |
| + | evalstring(p, ~SKIPEVAL); |
| + | |
| + | } |
| + | return exitstatus; |
| + | } |
| + | |
| + | /* |
| + | * Read and execute commands. "Top" is nonzero for the top level command |
| + | * loop; it turns on prompting if the shell is interactive. |
| + | */ |
| + | static int |
| + | cmdloop(int top) |
| + | { |
| + | union node *n; |
| + | struct stackmark smark; |
| + | int inter; |
| + | int numeof = 0; |
| + | |
| + | TRACE(("cmdloop(%d) called\n", top)); |
| + | for (;;) { |
| + | int skip; |
| + | |
| + | setstackmark(&smark); |
| + | #if JOBS |
| + | if (jobctl) |
| + | showjobs(stderr, SHOW_CHANGED); |
| + | #endif |
| + | inter = 0; |
| + | if (iflag && top) { |
| + | inter++; |
| + | #if ENABLE_ASH_MAIL |
| + | chkmail(); |
| + | #endif |
| + | } |
| + | n = parsecmd(inter); |
| + | /* showtree(n); DEBUG */ |
| + | if (n == NEOF) { |
| + | if (!top || numeof >= 50) |
| + | break; |
| + | if (!stoppedjobs()) { |
| + | if (!Iflag) |
| + | break; |
| + | out2str("\nUse \"exit\" to leave shell.\n"); |
| + | } |
| + | numeof++; |
| + | } else if (nflag == 0) { |
| + | job_warning = (job_warning == 2) ? 1 : 0; |
| + | numeof = 0; |
| + | evaltree(n, 0); |
| + | } |
| + | popstackmark(&smark); |
| + | skip = evalskip; |
| + | |
| + | if (skip) { |
| + | evalskip = 0; |
| + | return skip & SKIPEVAL; |
| + | } |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | static int |
| + | dotcmd(int argc, char **argv) |
| + | { |
| + | struct strlist *sp; |
| + | volatile struct shparam saveparam; |
| + | int status = 0; |
| + | |
| + | for (sp = cmdenviron; sp; sp = sp->next) |
| + | setvareq(xstrdup(sp->text), VSTRFIXED | VTEXTFIXED); |
| + | |
| + | if (argc >= 2) { /* That's what SVR2 does */ |
| + | char *fullname; |
| + | |
| + | fullname = find_dot_file(argv[1]); |
| + | |
| + | if (argc > 2) { |
| + | saveparam = shellparam; |
| + | shellparam.malloc = 0; |
| + | shellparam.nparam = argc - 2; |
| + | shellparam.p = argv + 2; |
| + | }; |
| + | |
| + | setinputfile(fullname, INPUT_PUSH_FILE); |
| + | commandname = fullname; |
| + | cmdloop(0); |
| + | popfile(); |
| + | |
| + | if (argc > 2) { |
| + | freeparam(&shellparam); |
| + | shellparam = saveparam; |
| + | }; |
| + | status = exitstatus; |
| + | } |
| + | return status; |
| + | } |
| + | |
| + | static int |
| + | exitcmd(int argc, char **argv) |
| + | { |
| + | if (stoppedjobs()) |
| + | return 0; |
| + | if (argc > 1) |
| + | exitstatus = number(argv[1]); |
| + | raise_exception(EXEXIT); |
| + | /* NOTREACHED */ |
| + | } |
| + | |
| + | #if ENABLE_ASH_BUILTIN_ECHO |
| + | static int |
| + | echocmd(int argc, char **argv) |
| + | { |
| + | return bb_echo(argv); |
| + | } |
| + | #endif |
| + | |
| + | #if ENABLE_ASH_BUILTIN_TEST |
| + | static int |
| + | testcmd(int argc, char **argv) |
| + | { |
| + | return bb_test(argc, argv); |
| + | } |
| + | #endif |
| + | |
| + | /* |
| + | * Read a file containing shell functions. |
| + | */ |
| + | static void |
| + | readcmdfile(char *name) |
| + | { |
| + | setinputfile(name, INPUT_PUSH_FILE); |
| + | cmdloop(0); |
| + | popfile(); |
| } | |
| + | |
| /* redir.c */ | |
| /* | |
| @@ -10477,7 +10407,8 @@ static const char *const *findkwd(const char *s) | |
| * Open a file in noclobber mode. | |
| * The code was copied from bash. | |
| */ | |
| - | static int noclobberopen(const char *fname) |
| + | static int |
| + | noclobberopen(const char *fname) |
| { | |
| int r, fd; | |
| struct stat finfo, finfo2; | |
| @@ -10536,7 +10467,8 @@ static int noclobberopen(const char *fname) | |
| * data to a pipe. If the document is short, we can stuff the data in | |
| * the pipe without forking. | |
| */ | |
| - | static int openhere(union node *redir) |
| + | static int |
| + | openhere(union node *redir) |
| { | |
| int pip[2]; | |
| size_t len = 0; | |
| @@ -10633,7 +10565,8 @@ openredirect(union node *redir) | |
| ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "No such file")); | |
| } | |
| - | static void dupredirect(union node *redir, int f) |
| + | static void |
| + | dupredirect(union node *redir, int f) |
| { | |
| int fd = redir->nfile.fd; | |
| @@ -11208,7 +11141,7 @@ trapcmd(int argc, char **argv) | |
| if (LONE_DASH(action)) | |
| action = NULL; | |
| else | |
| - | action = savestr(action); |
| + | action = ckstrdup(action); |
| } | |
| if (trap[signo]) | |
| free(trap[signo]); | |
| @@ -11614,7 +11547,7 @@ setvareq(char *s, int flags) | |
| *vpp = vp; | |
| } | |
| if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE))) | |
| - | s = savestr(s); |
| + | s = ckstrdup(s); |
| vp->text = s; | |
| vp->flags = flags; | |
| } | |
| @@ -13311,6 +13244,26 @@ read_profile(const char *name) | |
| exitshell(); | |
| } | |
| + | /* |
| + | * This routine is called when an error or an interrupt occurs in an |
| + | * interactive shell and control is returned to the main command loop. |
| + | */ |
| + | static void |
| + | reset(void) |
| + | { |
| + | /* from eval.c: */ |
| + | evalskip = 0; |
| + | loopnest = 0; |
| + | /* from input.c: */ |
| + | parselleft = parsenleft = 0; /* clear input buffer */ |
| + | popallfiles(); |
| + | /* from parser.c: */ |
| + | tokpushback = 0; |
| + | checkkwd = 0; |
| + | /* from redir.c: */ |
| + | clearredir(0); |
| + | } |
| + | |
| #if PROFILE | |
| static short profile_buf[16384]; | |
| extern int etext(); | |
| @@ -13404,9 +13357,9 @@ int ash_main(int argc, char **argv) | |
| state = 3; | |
| if ( | |
| #ifndef linux | |
| - | getuid() == geteuid() && getgid() == getegid() && |
| + | getuid() == geteuid() && getgid() == getegid() && |
| #endif | |
| - | iflag |
| + | iflag |
| ) { | |
| shinit = lookupvar("ENV"); | |
| if (shinit != NULL && *shinit != '\0') { | |