int evalcmd(shinstance *psh, int argc, char **argv) { char *p; char *concat; char **ap; if (argc > 1) { p = argv[1]; if (argc > 2) { STARTSTACKSTR(psh, concat); ap = argv + 2; for (;;) { while (*p) STPUTC(psh, *p++, concat); if ((p = *ap++) == NULL) break; STPUTC(psh, ' ', concat); } STPUTC(psh, '\0', concat); p = grabstackstr(psh, concat); } evalstring(psh, p, EV_TESTED); } return psh->exitstatus; }
/* * Perform expansions on an argument, placing the resulting list of arguments * in arglist. Parameter expansion, command substitution and arithmetic * expansion are always performed; additional expansions can be requested * via flag (EXP_*). * The result is left in the stack string. * When arglist is NULL, perform here document expansion. * * When doing something that may cause this to be re-entered, make sure * the stack string is empty via grabstackstr() and do not assume expdest * remains valid. */ void expandarg(union node *arg, struct arglist *arglist, int flag) { struct worddest exparg; struct nodelist *argbackq; if (fflag) flag &= ~EXP_GLOB; argbackq = arg->narg.backquote; exparg.list = arglist; exparg.state = WORD_IDLE; STARTSTACKSTR(expdest); argstr(arg->narg.text, &argbackq, flag, &exparg); if (arglist == NULL) { STACKSTRNUL(expdest); return; /* here document expanded */ } if ((flag & EXP_SPLIT) == 0 || expdest != stackblock() || exparg.state == WORD_QUOTEMARK) { STPUTC('\0', expdest); if (flag & EXP_SPLIT) { if (flag & EXP_GLOB) expandmeta(grabstackstr(expdest), exparg.list); else appendarglist(exparg.list, grabstackstr(expdest)); } } if ((flag & EXP_SPLIT) == 0) appendarglist(arglist, grabstackstr(expdest)); }
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); if ((p = *ap++) == NULL) break; STPUTC(' ', concat); } STPUTC('\0', concat); p = grabstackstr(concat); } return evalstring(p, ~SKIPEVAL); } return 0; }
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 (;;) { while (*p) STPUTC(*p++, concat); if ((p = *ap++) == NULL) break; STPUTC(' ', concat); } STPUTC('\0', concat); p = grabstackstr(concat); } evalstring(p); } return exitstatus; }
char * padvance(const char **path, const char *name) { const char *p, *start; char *q; int len; if (*path == NULL) return NULL; start = *path; for (p = start; *p && *p != ':' && *p != '%'; p++) ; /* nothing */ len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ STARTSTACKSTR(q); CHECKSTRSPACE(len, q); if (p != start) { memcpy(q, start, p - start); q += p - start; *q++ = '/'; } strcpy(q, name); pathopt = NULL; if (*p == '%') { pathopt = ++p; while (*p && *p != ':') p++; } if (*p == ':') *path = p + 1; else *path = NULL; return stalloc(len); }
/* * The exp(1) builtin. */ int letcmd(int argc, char **argv) { const char *p; char *concat; char **ap; arith_t i; if (argc > 1) { p = argv[1]; if (argc > 2) { /* * Concatenate arguments. */ STARTSTACKSTR(concat); ap = argv + 2; for (;;) { while (*p) STPUTC(*p++, concat); if ((p = *ap++) == NULL) break; STPUTC(' ', concat); } STPUTC('\0', concat); p = grabstackstr(concat); } } else p = ""; i = arith(p); out1fmt(ARITH_FORMAT_STR "\n", i); return !i; }
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 (;;) { STPUTS(p, concat); if ((p = *ap++) == NULL) break; STPUTC(' ', concat); } STPUTC('\0', concat); p = grabstackstr(concat); } evalstring(p, builtin_flags); } else exitstatus = 0; return exitstatus; }
int readcmd(int argc, char **argv) { char **ap; int backslash; char c; int rflag; char *prompt; char *p; int status; int i; rflag = 0; prompt = NULL; while ((i = nextopt("p:r")) != '\0') { if (i == 'p') prompt = optionarg; else rflag = 1; } if (prompt && isatty(0)) { out2str(prompt); #ifdef FLUSHERR flushall(); #endif } if (*(ap = argptr) == NULL) sh_error("arg count"); status = 0; backslash = 0; STARTSTACKSTR(p); for (;;) { if (read(0, &c, 1) != 1) { status = 1; break; } if (c == '\0') continue; if (backslash) { if (c == '\n') goto resetbs; STPUTC(CTLESC, p); goto put; } if (!rflag && c == '\\') { backslash++; continue; } if (c == '\n') break; put: STPUTC(c, p); resetbs: backslash = 0; } STACKSTRNUL(p); readcmd_handle_line(stackblock(), ap, p + 1 - (char *)stackblock()); return status; }
/* * Perform expansions on an argument, placing the resulting list of arguments * in arglist. Parameter expansion, command substitution and arithmetic * expansion are always performed; additional expansions can be requested * via flag (EXP_*). * The result is left in the stack string. * When arglist is NULL, perform here document expansion. * * Caution: this function uses global state and is not reentrant. * However, a new invocation after an interrupted invocation is safe * and will reset the global state for the new call. */ void expandarg(union node* arg, struct arglist* arglist, int32_t flag) { struct strlist* sp; cstring_t p; argbackq = arg->narg.backquote; STARTSTACKSTR(expdest); ifsfirst.next = NULL; ifslastp = NULL; argstr(arg->narg.text, flag); if (arglist == NULL) { STACKSTRNUL(expdest); return; /* here document expanded */ } STPUTC('\0', expdest); p = grabstackstr(expdest); exparg.lastp = &exparg.list; /* * TODO - EXP_REDIR */ if (flag & EXP_FULL) { ifsbreakup(p, &exparg); *exparg.lastp = NULL; exparg.lastp = &exparg.list; expandmeta(exparg.list, flag); } else { if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ rmescapes(p); sp = (struct strlist*)stalloc(sizeof(struct strlist)); sp->text = p; *exparg.lastp = sp; exparg.lastp = &sp->next; } while (ifsfirst.next != NULL) { pifsregion_t ifsp; INTOFF; ifsp = ifsfirst.next->next; ckfree(ifsfirst.next); ifsfirst.next = ifsp; INTON; } *exparg.lastp = NULL; if (exparg.list) { *arglist->lastp = exparg.list; arglist->lastp = exparg.lastp; } }
/* * Print SysV echo(1) style escape string * Halts processing string if a \c escape is encountered. */ static char * conv_escape_str(char *str) { int ch; char *cp; /* convert string into a temporary buffer... */ STARTSTACKSTR(cp); do { int c; ch = *str++; if (ch != '\\') continue; ch = *str++; if (ch == 'c') { /* \c as in SYSV echo - abort all processing.... */ rval |= 0x100; ch = 0; continue; } /* * %b string octal constants are not like those in C. * They start with a \0, and are followed by 0, 1, 2, * or 3 octal digits. */ if (ch == '0') { unsigned char i; i = 3; ch = 0; do { unsigned k = octtobin(*str); if (k > 7) break; str++; ch <<= 3; ch += k; } while (--i); continue; } /* Finally test for sequences valid in the format string */ str = conv_escape(str - 1, &c); ch = c; } while (STPUTC(ch, cp), ch); return stackblock(); }
static char * mklong(const char *str, const char *ch) { char *copy; size_t len; len = ch - str + 3; STARTSTACKSTR(copy); copy = makestrspace(len, copy); memcpy(copy, str, len - 3); copy[len - 3] = 'j'; copy[len - 2] = *ch; copy[len - 1] = '\0'; return (copy); }
static char * mklong(const char *str, const char *ch) { char *copy; size_t len; size_t pridmax_len = strlen(PRIdMAX); len = ch - str + pridmax_len; STARTSTACKSTR(copy); copy = makestrspace(len + 1, copy); memcpy(copy, str, len - pridmax_len); memcpy(copy + len - pridmax_len, PRIdMAX, pridmax_len - 1); copy[len - 1] = *ch; copy[len] = '\0'; return (copy); }
int32_t casematch(union node* pattern, const_cstring_t val) { struct stackmark smark; int32_t result; cstring_t p; setstackmark(&smark); argbackq = pattern->narg.backquote; STARTSTACKSTR(expdest); ifslastp = NULL; argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); STPUTC('\0', expdest); p = grabstackstr(expdest); result = patmatch(p, val, 0); popstackmark(&smark); return result; }
int casematch(union node *pattern, const char *val) { struct stackmark smark; int result; char *p; setstackmark(&smark); argbackq = pattern->narg.backquote; STARTSTACKSTR(expdest); argstr(pattern->narg.text, EXP_TILDE | EXP_CASE, NULL); STPUTC('\0', expdest); p = grabstackstr(expdest); result = patmatch(p, val); popstackmark(&smark); return result; }
int casematch(shinstance *psh, union node *pattern, char *val) { struct stackmark smark; int result; char *p; setstackmark(psh, &smark); psh->argbackq = pattern->narg.backquote; STARTSTACKSTR(psh, psh->expdest); psh->ifslastp = NULL; argstr(psh, pattern->narg.text, EXP_TILDE | EXP_CASE); STPUTC(psh, '\0', psh->expdest); p = grabstackstr(psh, psh->expdest); result = patmatch(psh, p, val, 0); popstackmark(psh, &smark); return result; }
/* * Perform expansions on an argument, placing the resulting list of arguments * in arglist. Parameter expansion, command substitution and arithmetic * expansion are always performed; additional expansions can be requested * via flag (EXP_*). * The result is left in the stack string. * When arglist is NULL, perform here document expansion. * * Caution: this function uses global state and is not reentrant. * However, a new invocation after an interrupted invocation is safe * and will reset the global state for the new call. */ void expandarg(union node *arg, struct arglist *arglist, int flag) { struct strlist *sp; char *p; argbackq = arg->narg.backquote; STARTSTACKSTR(expdest); ifsfirst.next = NULL; ifslastp = NULL; argstr(arg->narg.text, flag); if (arglist == NULL) { STACKSTRNUL(expdest); return; /* here document expanded */ } STPUTC('\0', expdest); p = grabstackstr(expdest); exparg.lastp = &exparg.list; if (flag & EXP_FULL) { ifsbreakup(p, &exparg); *exparg.lastp = NULL; exparg.lastp = &exparg.list; expandmeta(exparg.list); } else { sp = (struct strlist *)stalloc(sizeof (struct strlist)); sp->text = p; *exparg.lastp = sp; exparg.lastp = &sp->next; } while (ifsfirst.next != NULL) { struct ifsregion *ifsp; INTOFF; ifsp = ifsfirst.next->next; ckfree(ifsfirst.next); ifsfirst.next = ifsp; INTON; } *exparg.lastp = NULL; if (exparg.list) { *arglist->lastp = exparg.list; arglist->lastp = exparg.lastp; } }
void expandarg(shinstance *psh, union node *arg, struct arglist *arglist, int flag) { struct strlist *sp; char *p; psh->argbackq = arg->narg.backquote; STARTSTACKSTR(psh, psh->expdest); psh->ifsfirst.next = NULL; psh->ifslastp = NULL; argstr(psh, arg->narg.text, flag); if (arglist == NULL) { return; /* here document expanded */ } STPUTC(psh, '\0', psh->expdest); p = grabstackstr(psh, psh->expdest); TRACE2((psh, "expandarg: p='%s'\n", p)); psh->exparg.lastp = &psh->exparg.list; /* * TODO - EXP_REDIR */ if (flag & EXP_FULL) { ifsbreakup(psh, p, &psh->exparg); *psh->exparg.lastp = NULL; psh->exparg.lastp = &psh->exparg.list; expandmeta(psh, psh->exparg.list, flag); } else { if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ rmescapes(psh, p); sp = (struct strlist *)stalloc(psh, sizeof (struct strlist)); sp->text = p; *psh->exparg.lastp = sp; psh->exparg.lastp = &sp->next; } ifsfree(psh); *psh->exparg.lastp = NULL; if (psh->exparg.list) { *arglist->lastp = psh->exparg.list; arglist->lastp = psh->exparg.lastp; } }
STATIC const char * fc_replace(const char *s, char *p, char *r) { char *dest; int plen = strlen(p); STARTSTACKSTR(dest); while (*s) { if (*s == *p && strncmp(s, p, plen) == 0) { while (*r) STPUTC(*r++, dest); s += plen; *p = '\0'; /* so no more matches */ } else STPUTC(*s++, dest); } STACKSTRNUL(dest); dest = grabstackstr(dest); return (dest); }
char * single_quote(const char *s) { char *p; STARTSTACKSTR(p); do { char *q; size_t len; len = strchrnul(s, '\'') - s; q = p = makestrspace(len + 3, p); *q++ = '\''; q = mempcpy(q, s, len); *q++ = '\''; s += len; STADJUST(q - p, p); len = strspn(s, "'"); if (!len) break; q = p = makestrspace(len + 3, p); *q++ = '"'; q = mempcpy(q, s, len); *q++ = '"'; s += len; STADJUST(q - p, p); } while (*s); USTPUTC(0, p); return stackblock(); }
static char * nextword(char c, int flag, char *p, struct worddest *dst) { int is_ws; is_ws = c == '\t' || c == '\n' || c == ' '; if (p != stackblock() || (is_ws ? dst->state == WORD_QUOTEMARK : dst->state != WORD_WS_DELIMITED) || c == '\0') { STPUTC('\0', p); if (flag & EXP_GLOB) expandmeta(grabstackstr(p), dst->list); else appendarglist(dst->list, grabstackstr(p)); dst->state = is_ws ? WORD_WS_DELIMITED : WORD_IDLE; } else if (!is_ws && dst->state == WORD_WS_DELIMITED) dst->state = WORD_IDLE; /* Reserve space while the stack string is empty. */ appendarglist(dst->list, NULL); dst->list->count--; STARTSTACKSTR(p); return p; }
static char * mklong(const char *str, const char *ch) { /* * Replace a string like "%92.3u" with "%92.3"PRIuMAX. * * Although C99 does not guarantee it, we assume PRIiMAX, * PRIoMAX, PRIuMAX, PRIxMAX, and PRIXMAX are all the same * as PRIdMAX with the final 'd' replaced by the corresponding * character. */ char *copy; size_t len; len = ch - str + sizeof(PRIdMAX); STARTSTACKSTR(copy); copy = makestrspace(len, copy); memcpy(copy, str, len - sizeof(PRIdMAX)); memcpy(copy + len - sizeof(PRIdMAX), PRIdMAX, sizeof(PRIdMAX)); copy[len - 2] = *ch; return (copy); }
int readcmd(int argc, char **argv) { char **ap; char c; int rflag; char *prompt; const char *ifs; char *p; int startword; int status; int i; int is_ifs; int saveall = 0; rflag = 0; prompt = NULL; while ((i = nextopt("p:r")) != '\0') { if (i == 'p') prompt = optionarg; else rflag = 1; } if (prompt && isatty(0)) { out2str(prompt); flushall(); } if (*(ap = argptr) == NULL) error("arg count"); if ((ifs = bltinlookup("IFS", 1)) == NULL) ifs = " \t\n"; status = 0; startword = 2; STARTSTACKSTR(p); for (;;) { if (read(0, &c, 1) != 1) { status = 1; break; } if (c == '\0') continue; if (c == '\\' && !rflag) { if (read(0, &c, 1) != 1) { status = 1; break; } if (c != '\n') STPUTC(c, p); continue; } if (c == '\n') break; if (strchr(ifs, c)) is_ifs = strchr(" \t\n", c) ? 1 : 2; else is_ifs = 0; if (startword != 0) { if (is_ifs == 1) { /* Ignore leading IFS whitespace */ if (saveall) STPUTC(c, p); continue; } if (is_ifs == 2 && startword == 1) { /* Only one non-whitespace IFS per word */ startword = 2; if (saveall) STPUTC(c, p); continue; } } if (is_ifs == 0) { /* append this character to the current variable */ startword = 0; if (saveall) /* Not just a spare terminator */ saveall++; STPUTC(c, p); continue; } /* end of variable... */ startword = is_ifs; if (ap[1] == NULL) { /* Last variable needs all IFS chars */ saveall++; STPUTC(c, p); continue; } STACKSTRNUL(p); setvar(*ap, stackblock(), 0); ap++; STARTSTACKSTR(p); } STACKSTRNUL(p); /* Remove trailing IFS chars */ for (; stackblock() <= --p; *p = 0) { if (!strchr(ifs, *p)) break; if (strchr(" \t\n", *p)) /* Always remove whitespace */ continue; if (saveall > 1) /* Don't remove non-whitespace unless it was naked */ break; } setvar(*ap, stackblock(), 0); /* Set any remaining args to "" */ while (*++ap != NULL) setvar(*ap, nullstr, 0); return status; }
int readcmd(int argc, char **argv) { char **ap; char c; int rflag; char *prompt; char *p; int startloc; int newloc; int status; int timeout; int i; fd_set set; struct timeval ts, t0, t1, to; ts.tv_sec = ts.tv_usec = 0; rflag = 0; timeout = 0; prompt = NULL; while ((i = nextopt("p:rt:")) != '\0') { switch(i) { case 'p': prompt = optionarg; break; case 't': p = strtotimeval(optionarg, &ts); if (*p || (!ts.tv_sec && !ts.tv_usec)) sh_error("invalid timeout"); timeout = 1; break; case 'r': rflag = 1; break; default: break; } } if (prompt && isatty(0)) { out2str(prompt); #ifdef FLUSHERR flushall(); #endif } if (*(ap = argptr) == NULL) sh_error("arg count"); status = 0; if (timeout) { gettimeofday(&t0, NULL); /* ts += t0; */ ts.tv_usec += t0.tv_usec; while (ts.tv_usec >= 1000000) { ts.tv_sec++; ts.tv_usec -= 1000000; } ts.tv_sec += t0.tv_sec; } STARTSTACKSTR(p); goto start; for (;;) { if (timeout) { gettimeofday(&t1, NULL); if (t1.tv_sec > ts.tv_sec || (t1.tv_sec == ts.tv_sec && t1.tv_usec >= ts.tv_usec)) { status = 1; break; /* Timeout! */ } /* to = ts - t1; */ if (ts.tv_usec >= t1.tv_usec) { to.tv_usec = ts.tv_usec - t1.tv_usec; to.tv_sec = ts.tv_sec - t1.tv_sec; } else { to.tv_usec = ts.tv_usec - t1.tv_usec + 1000000; to.tv_sec = ts.tv_sec - t1.tv_sec - 1; } FD_ZERO(&set); FD_SET(0, &set); if (select(1, &set, NULL, NULL, &to) != 1) { status = 1; break; /* Timeout! */ } } switch (read(0, &c, 1)) { case 1: break; default: if (errno == EINTR && !pendingsigs) continue; /* fall through */ case 0: status = 1; goto out; } if (c == '\0') continue; if (newloc >= startloc) { if (c == '\n') goto resetbs; goto put; } if (!rflag && c == '\\') { newloc = p - (char *)stackblock(); continue; } if (c == '\n') break; put: CHECKSTRSPACE(2, p); if (strchr(qchars, c)) USTPUTC(CTLESC, p); USTPUTC(c, p); if (newloc >= startloc) { resetbs: recordregion(startloc, newloc, 0); start: startloc = p - (char *)stackblock(); newloc = startloc - 1; } } out: recordregion(startloc, p - (char *)stackblock(), 0); STACKSTRNUL(p); readcmd_handle_line(p + 1, ap); return status; }
int readcmd(int argc, char **argv) { char **ap; char c; int rflag; char *prompt; char *p; int startloc; int newloc; int status; int i; rflag = 0; prompt = NULL; while ((i = nextopt("p:r")) != '\0') { if (i == 'p') prompt = optionarg; else rflag = 1; } if (prompt && isatty(0)) { out2str(prompt); #ifdef FLUSHERR flushall(); #endif } if (*(ap = argptr) == NULL) sh_error("arg count"); status = 0; STARTSTACKSTR(p); goto start; for (;;) { switch (read(0, &c, 1)) { case 1: break; default: if (errno == EINTR && !pendingsigs) continue; /* fall through */ case 0: status = 1; goto out; } if (c == '\0') continue; if (newloc >= startloc) { if (c == '\n') goto resetbs; goto put; } if (!rflag && c == '\\') { newloc = p - (char *)stackblock(); continue; } if (c == '\n') break; put: CHECKSTRSPACE(2, p); if (strchr(qchars, c)) USTPUTC(CTLESC, p); USTPUTC(c, p); if (newloc >= startloc) { resetbs: recordregion(startloc, newloc, 0); start: startloc = p - (char *)stackblock(); newloc = startloc - 1; } } out: recordregion(startloc, p - (char *)stackblock(), 0); STACKSTRNUL(p); readcmd_handle_line(p + 1, ap); return status; }
STATIC int readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) { int c = firstc; char *out; int len; char line[EOFMARKLEN + 1]; struct nodelist *bqlist; int quotef; int dblquote; int varnest; /* levels of variables expansion */ int arinest; /* levels of arithmetic expansion */ int parenlevel; /* levels of parens in arithmetic */ int oldstyle; char const *prevsyntax; /* syntax before arithmetic */ int synentry; #if __GNUC__ /* Avoid longjmp clobbering */ (void) &out; (void) "ef; (void) &dblquote; (void) &varnest; (void) &arinest; (void) &parenlevel; (void) &oldstyle; (void) &prevsyntax; (void) &syntax; (void) &synentry; #endif startlinno = plinno; dblquote = 0; if (syntax == DQSYNTAX) dblquote = 1; quotef = 0; bqlist = NULL; varnest = 0; arinest = 0; parenlevel = 0; STARTSTACKSTR(out); loop: { /* for each line, until end of word */ CHECKEND(); /* set c to PEOF if at end of here document */ for (;;) { /* until end of line or end of word */ CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */ synentry = syntax[c]; switch(synentry) { case CNL: /* '\n' */ if (syntax == BASESYNTAX) goto endword; /* exit outer loop */ USTPUTC(c, out); plinno++; if (doprompt) setprompt(2); else setprompt(0); c = pgetc(); goto loop; /* continue outer loop */ case CWORD: USTPUTC(c, out); break; case CCTL: if (eofmark == NULL || dblquote) USTPUTC(CTLESC, out); USTPUTC(c, out); break; case CBACK: /* backslash */ c = pgetc(); if (c == PEOF) { USTPUTC('\\', out); pungetc(); } else if (c == '\n') { if (doprompt) setprompt(2); else setprompt(0); } else { if (dblquote && c != '\\' && c != '`' && c != '$' && (c != '"' || eofmark != NULL)) USTPUTC('\\', out); if (SQSYNTAX[c] == CCTL) USTPUTC(CTLESC, out); else if (eofmark == NULL) USTPUTC(CTLQUOTEMARK, out); USTPUTC(c, out); quotef++; } break; case CSQUOTE: if (eofmark == NULL) USTPUTC(CTLQUOTEMARK, out); syntax = SQSYNTAX; break; case CDQUOTE: if (eofmark == NULL) USTPUTC(CTLQUOTEMARK, out); syntax = DQSYNTAX; dblquote = 1; break; case CENDQUOTE: if (eofmark != NULL && arinest == 0 && varnest == 0) { USTPUTC(c, out); } else { if (arinest) { syntax = ARISYNTAX; dblquote = 0; } else if (eofmark == NULL) { syntax = BASESYNTAX; dblquote = 0; } quotef++; } break; case CVAR: /* '$' */ PARSESUB(); /* parse substitution */ break; case CENDVAR: /* '}' */ if (varnest > 0) { varnest--; USTPUTC(CTLENDVAR, out); } else { USTPUTC(c, out); } break; case CLP: /* '(' in arithmetic */ parenlevel++; USTPUTC(c, out); break; case CRP: /* ')' in arithmetic */ if (parenlevel > 0) { USTPUTC(c, out); --parenlevel; } else { if (pgetc() == ')') { if (--arinest == 0) { USTPUTC(CTLENDARI, out); syntax = prevsyntax; if (syntax == DQSYNTAX) dblquote = 1; else dblquote = 0; } else USTPUTC(')', out); } else { /* * unbalanced parens * (don't 2nd guess - no error) */ pungetc(); USTPUTC(')', out); } } break; case CBQUOTE: /* '`' */ PARSEBACKQOLD(); break; case CEOF: goto endword; /* exit outer loop */ default: if (varnest == 0) goto endword; /* exit outer loop */ USTPUTC(c, out); } c = pgetc_macro(); } } endword: if (syntax == ARISYNTAX) synerror("Missing '))'"); if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL) synerror("Unterminated quoted string"); if (varnest != 0) { startlinno = plinno; synerror("Missing '}'"); } USTPUTC('\0', out); len = out - stackblock(); out = stackblock(); if (eofmark == NULL) { if ((c == '>' || c == '<') && quotef == 0 && len <= 2 && (*out == '\0' || is_digit(*out))) { PARSEREDIR(); return lasttoken = TREDIR; } else { pungetc(); } } quoteflag = quotef; backquotelist = bqlist; grabstackblock(len); wordtext = out; return lasttoken = TWORD; /* end of readtoken routine */ /* * Check to see whether we are at the end of the here document. When this * is called, c is set to the first character of the next input line. If * we are at the end of the here document, this routine sets the c to PEOF. */ checkend: { if (eofmark) { if (striptabs) { while (c == '\t') c = pgetc(); } if (c == *eofmark) { if (pfgets(line, sizeof line) != NULL) { char *p, *q; p = line; for (q = eofmark + 1 ; *q && *p == *q ; p++, q++); if (*p == '\n' && *q == '\0') { c = PEOF; plinno++; needprompt = doprompt; } else { pushstring(line, strlen(line), NULL); } } } } goto checkend_return; } /* * Parse a redirection operator. The variable "out" points to a string * specifying the fd to be redirected. The variable "c" contains the * first character of the redirection operator. */ parseredir: { char fd = *out; union node *np; np = (union node *)stalloc(sizeof (struct nfile)); if (c == '>') { np->nfile.fd = 1; c = pgetc(); if (c == '>') np->type = NAPPEND; else if (c == '&') np->type = NTOFD; else if (c == '|') np->type = NCLOBBER; else { np->type = NTO; pungetc(); } } else { /* c == '<' */ np->nfile.fd = 0; c = pgetc(); if (c == '<') { if (sizeof (struct nfile) != sizeof (struct nhere)) { np = (union node *)stalloc(sizeof (struct nhere)); np->nfile.fd = 0; } np->type = NHERE; heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc)); heredoc->here = np; if ((c = pgetc()) == '-') { heredoc->striptabs = 1; } else { heredoc->striptabs = 0; pungetc(); } } else if (c == '&') np->type = NFROMFD; else if (c == '>') np->type = NFROMTO; else { np->type = NFROM; pungetc(); } } if (fd != '\0') np->nfile.fd = digit_val(fd); redirnode = np; goto parseredir_return; } /* * Parse a substitution. At this point, we have read the dollar sign * and nothing else. */ parsesub: { int subtype; int typeloc; int flags; char *p; #ifndef GDB_HACK static const char types[] = "}-+?="; #endif int bracketed_name = 0; /* used to handle ${[0-9]*} variables */ c = pgetc(); if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) { USTPUTC('$', out); pungetc(); } else if (c == '(') { /* $(command) or $((arith)) */ if (pgetc() == '(') { PARSEARITH(); } else { pungetc(); PARSEBACKQNEW(); } } else { USTPUTC(CTLVAR, out); typeloc = out - stackblock(); USTPUTC(VSNORMAL, out); subtype = VSNORMAL; if (c == '{') { bracketed_name = 1; c = pgetc(); if (c == '#') { if ((c = pgetc()) == '}') c = '#'; else subtype = VSLENGTH; } else subtype = 0; } if (is_name(c)) { do { STPUTC(c, out); c = pgetc(); } while (is_in_name(c)); } else if (is_digit(c)) { if (bracketed_name) { do { STPUTC(c, out); c = pgetc(); } while (is_digit(c)); } else { STPUTC(c, out); c = pgetc(); } } else { if (! is_special(c)) badsub: synerror("Bad substitution"); USTPUTC(c, out); c = pgetc(); } STPUTC('=', out); flags = 0; if (subtype == 0) { switch (c) { case ':': flags = VSNUL; c = pgetc(); /*FALLTHROUGH*/ default: p = strchr(types, c); if (p == NULL) goto badsub; subtype = p - types + VSNORMAL; break; case '%': case '#': { int cc = c; subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT; c = pgetc(); if (c == cc) subtype++; else pungetc(); break; } } } else { pungetc(); } if (subtype != VSLENGTH && (dblquote || arinest)) flags |= VSQUOTE; *(stackblock() + typeloc) = subtype | flags; if (subtype != VSNORMAL) varnest++; } goto parsesub_return; } /* * Called to parse command substitutions. Newstyle is set if the command * is enclosed inside $(...); nlpp is a pointer to the head of the linked * list of commands (passed by reference), and savelen is the number of * characters on the top of the stack which must be preserved. */ parsebackq: { struct nodelist **nlpp; int savepbq; union node *n; char *volatile str; struct jmploc jmploc; struct jmploc *volatile savehandler; int savelen; int saveprompt; #if __GNUC__ /* Avoid longjmp clobbering */ (void) &saveprompt; #endif savepbq = parsebackquote; if (setjmp(jmploc.loc)) { if (str) ckfree(str); parsebackquote = 0; handler = savehandler; longjmp(handler->loc, 1); } INTOFF; str = NULL; savelen = out - stackblock(); if (savelen > 0) { str = ckmalloc(savelen); memcpy(str, stackblock(), savelen); } savehandler = handler; handler = &jmploc; INTON; if (oldstyle) { /* We must read until the closing backquote, giving special treatment to some slashes, and then push the string and reread it as input, interpreting it normally. */ char *out; int c; int savelen; char *str; STARTSTACKSTR(out); for (;;) { if (needprompt) { setprompt(2); needprompt = 0; } switch (c = pgetc()) { case '`': goto done; case '\\': if ((c = pgetc()) == '\n') { plinno++; if (doprompt) setprompt(2); else setprompt(0); /* * If eating a newline, avoid putting * the newline into the new character * stream (via the STPUTC after the * switch). */ continue; } if (c != '\\' && c != '`' && c != '$' && (!dblquote || c != '"')) STPUTC('\\', out); break; case '\n': plinno++; needprompt = doprompt; break; case PEOF: startlinno = plinno; synerror("EOF in backquote substitution"); break; default: break; } STPUTC(c, out); } done: STPUTC('\0', out); savelen = out - stackblock(); if (savelen > 0) { str = ckmalloc(savelen); memcpy(str, stackblock(), savelen); setinputstring(str, 1); } } nlpp = &bqlist; while (*nlpp) nlpp = &(*nlpp)->next; *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); (*nlpp)->next = NULL; parsebackquote = oldstyle; if (oldstyle) { saveprompt = doprompt; doprompt = 0; } n = list(0); if (oldstyle) doprompt = saveprompt; else { if (readtoken() != TRP) synexpect(TRP); } (*nlpp)->n = n; if (oldstyle) { /* * Start reading from old file again, ignoring any pushed back * tokens left from the backquote parsing */ popfile(); tokpushback = 0; } while (stackblocksize() <= savelen) growstackblock(); STARTSTACKSTR(out); if (str) { memcpy(out, str, savelen); STADJUST(savelen, out); INTOFF; ckfree(str); str = NULL; INTON; } parsebackquote = savepbq; handler = savehandler; if (arinest || dblquote) USTPUTC(CTLBACKQ | CTLQUOTE, out); else USTPUTC(CTLBACKQ, out); if (oldstyle) goto parsebackq_oldreturn; else goto parsebackq_newreturn; } /* * Parse an arithmetic expansion (indicate start of one and set state) */ parsearith: { if (++arinest == 1) { prevsyntax = syntax; syntax = ARISYNTAX; USTPUTC(CTLARI, out); if (dblquote) USTPUTC('"',out); else USTPUTC(' ',out); } else { /* * we collapse embedded arithmetic expansion to * parenthesis, which should be equivalent */ USTPUTC('(', out); } goto parsearith_return; } } /* end of readtoken */