Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 1 | /* $OpenBSD: syn.c,v 1.30 2015/09/01 13:12:31 tedu Exp $ */ |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 2 | |
| 3 | /*- |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 4 | * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 5 | * 2011, 2012, 2013, 2014, 2015, 2016, 2017 |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 6 | * mirabilos <m@mirbsd.org> |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 7 | * |
| 8 | * Provided that these terms and disclaimer and all copyright notices |
| 9 | * are retained or reproduced in an accompanying document, permission |
| 10 | * is granted to deal in this work without restriction, including un- |
| 11 | * limited rights to use, publicly perform, distribute, sell, modify, |
| 12 | * merge, give away, or sublicence. |
| 13 | * |
| 14 | * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to |
| 15 | * the utmost extent permitted by applicable law, neither express nor |
| 16 | * implied; without malicious intent or gross negligence. In no event |
| 17 | * may a licensor, author or contributor be held liable for indirect, |
| 18 | * direct, other damage, loss, or other issues arising in any way out |
| 19 | * of dealing in the work, even if advised of the possibility of such |
| 20 | * damage or existence of a defect, except proven that it results out |
| 21 | * of said person's immediate fault when using the work as intended. |
| 22 | */ |
| 23 | |
| 24 | #include "sh.h" |
| 25 | |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 26 | __RCSID("$MirOS: src/bin/mksh/syn.c,v 1.120 2017/04/06 01:59:57 tg Exp $"); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 27 | |
| 28 | struct nesting_state { |
| 29 | int start_token; /* token than began nesting (eg, FOR) */ |
| 30 | int start_line; /* line nesting began on */ |
| 31 | }; |
| 32 | |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 33 | struct yyrecursive_state { |
Elliott Hughes | 77740fc | 2016-08-12 15:06:53 -0700 | [diff] [blame] | 34 | struct ioword *old_heres[HERES]; |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 35 | struct yyrecursive_state *next; |
| 36 | struct ioword **old_herep; |
| 37 | int old_symbol; |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 38 | int old_nesting_type; |
| 39 | bool old_reject; |
| 40 | }; |
| 41 | |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 42 | static void yyparse(bool); |
| 43 | static struct op *pipeline(int, int); |
| 44 | static struct op *andor(int); |
| 45 | static struct op *c_list(int, bool); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 46 | static struct ioword *synio(int); |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 47 | static struct op *nested(int, int, int, int); |
| 48 | static struct op *get_command(int, int); |
| 49 | static struct op *dogroup(int); |
| 50 | static struct op *thenpart(int); |
| 51 | static struct op *elsepart(int); |
| 52 | static struct op *caselist(int); |
| 53 | static struct op *casepart(int, int); |
| 54 | static struct op *function_body(char *, int, bool); |
| 55 | static char **wordlist(int); |
Thorsten Glaser | 811a575 | 2013-07-25 14:24:45 +0000 | [diff] [blame] | 56 | static struct op *block(int, struct op *, struct op *); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 57 | static struct op *newtp(int); |
| 58 | static void syntaxerr(const char *) MKSH_A_NORETURN; |
| 59 | static void nesting_push(struct nesting_state *, int); |
| 60 | static void nesting_pop(struct nesting_state *); |
Elliott Hughes | 737fdce | 2014-08-07 12:59:26 -0700 | [diff] [blame] | 61 | static int inalias(struct source *) MKSH_A_PURE; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 62 | static Test_op dbtestp_isa(Test_env *, Test_meta); |
| 63 | static const char *dbtestp_getopnd(Test_env *, Test_op, bool); |
| 64 | static int dbtestp_eval(Test_env *, Test_op, const char *, |
| 65 | const char *, bool); |
| 66 | static void dbtestp_error(Test_env *, int, const char *) MKSH_A_NORETURN; |
| 67 | |
| 68 | static struct op *outtree; /* yyparse output */ |
| 69 | static struct nesting_state nesting; /* \n changed to ; */ |
| 70 | |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 71 | static bool reject; /* token(cf) gets symbol again */ |
| 72 | static int symbol; /* yylex value */ |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 73 | |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 74 | #define REJECT (reject = true) |
| 75 | #define ACCEPT (reject = false) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 76 | #define token(cf) ((reject) ? (ACCEPT, symbol) : (symbol = yylex(cf))) |
| 77 | #define tpeek(cf) ((reject) ? (symbol) : (REJECT, symbol = yylex(cf))) |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 78 | #define musthave(c,cf) do { if (token(cf) != (c)) syntaxerr(NULL); } while (/* CONSTCOND */ 0) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 79 | |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 80 | static const char Tcbrace[] = "}"; |
| 81 | static const char Tesac[] = "esac"; |
| 82 | |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 83 | static void |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 84 | yyparse(bool doalias) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 85 | { |
| 86 | int c; |
| 87 | |
| 88 | ACCEPT; |
| 89 | |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 90 | outtree = c_list(doalias ? ALIAS : 0, source->type == SSTRING); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 91 | c = tpeek(0); |
| 92 | if (c == 0 && !outtree) |
| 93 | outtree = newtp(TEOF); |
| 94 | else if (c != '\n' && c != 0) |
| 95 | syntaxerr(NULL); |
| 96 | } |
| 97 | |
| 98 | static struct op * |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 99 | pipeline(int cf, int sALIAS) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 100 | { |
| 101 | struct op *t, *p, *tl = NULL; |
| 102 | |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 103 | t = get_command(cf, sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 104 | if (t != NULL) { |
| 105 | while (token(0) == '|') { |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 106 | if ((p = get_command(CONTIN, sALIAS)) == NULL) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 107 | syntaxerr(NULL); |
| 108 | if (tl == NULL) |
Thorsten Glaser | 811a575 | 2013-07-25 14:24:45 +0000 | [diff] [blame] | 109 | t = tl = block(TPIPE, t, p); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 110 | else |
Thorsten Glaser | 811a575 | 2013-07-25 14:24:45 +0000 | [diff] [blame] | 111 | tl = tl->right = block(TPIPE, tl->right, p); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 112 | } |
| 113 | REJECT; |
| 114 | } |
| 115 | return (t); |
| 116 | } |
| 117 | |
| 118 | static struct op * |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 119 | andor(int sALIAS) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 120 | { |
| 121 | struct op *t, *p; |
| 122 | int c; |
| 123 | |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 124 | t = pipeline(0, sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 125 | if (t != NULL) { |
| 126 | while ((c = token(0)) == LOGAND || c == LOGOR) { |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 127 | if ((p = pipeline(CONTIN, sALIAS)) == NULL) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 128 | syntaxerr(NULL); |
Thorsten Glaser | 811a575 | 2013-07-25 14:24:45 +0000 | [diff] [blame] | 129 | t = block(c == LOGAND? TAND: TOR, t, p); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 130 | } |
| 131 | REJECT; |
| 132 | } |
| 133 | return (t); |
| 134 | } |
| 135 | |
| 136 | static struct op * |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 137 | c_list(int sALIAS, bool multi) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 138 | { |
| 139 | struct op *t = NULL, *p, *tl = NULL; |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 140 | int c; |
| 141 | bool have_sep; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 142 | |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 143 | while (/* CONSTCOND */ 1) { |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 144 | p = andor(sALIAS); |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 145 | /* |
| 146 | * Token has always been read/rejected at this point, so |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 147 | * we don't worry about what flags to pass token() |
| 148 | */ |
| 149 | c = token(0); |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 150 | have_sep = true; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 151 | if (c == '\n' && (multi || inalias(source))) { |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 152 | if (!p) |
| 153 | /* ignore blank lines */ |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 154 | continue; |
| 155 | } else if (!p) |
| 156 | break; |
| 157 | else if (c == '&' || c == COPROC) |
Thorsten Glaser | 811a575 | 2013-07-25 14:24:45 +0000 | [diff] [blame] | 158 | p = block(c == '&' ? TASYNC : TCOPROC, p, NULL); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 159 | else if (c != ';') |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 160 | have_sep = false; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 161 | if (!t) |
| 162 | t = p; |
| 163 | else if (!tl) |
Thorsten Glaser | 811a575 | 2013-07-25 14:24:45 +0000 | [diff] [blame] | 164 | t = tl = block(TLIST, t, p); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 165 | else |
Thorsten Glaser | 811a575 | 2013-07-25 14:24:45 +0000 | [diff] [blame] | 166 | tl = tl->right = block(TLIST, tl->right, p); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 167 | if (!have_sep) |
| 168 | break; |
| 169 | } |
| 170 | REJECT; |
| 171 | return (t); |
| 172 | } |
| 173 | |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 174 | static const char IONDELIM_delim[] = { CHAR, '<', CHAR, '<', EOS }; |
| 175 | |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 176 | static struct ioword * |
| 177 | synio(int cf) |
| 178 | { |
| 179 | struct ioword *iop; |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 180 | static struct ioword *nextiop; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 181 | bool ishere; |
| 182 | |
| 183 | if (nextiop != NULL) { |
| 184 | iop = nextiop; |
| 185 | nextiop = NULL; |
| 186 | return (iop); |
| 187 | } |
| 188 | |
| 189 | if (tpeek(cf) != REDIR) |
| 190 | return (NULL); |
| 191 | ACCEPT; |
| 192 | iop = yylval.iop; |
Elliott Hughes | b27ce95 | 2015-04-21 13:39:18 -0700 | [diff] [blame] | 193 | ishere = (iop->ioflag & IOTYPE) == IOHERE; |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 194 | if (iop->ioflag & IOHERESTR) { |
| 195 | musthave(LWORD, 0); |
| 196 | } else if (ishere && tpeek(HEREDELIM) == '\n') { |
| 197 | ACCEPT; |
| 198 | yylval.cp = wdcopy(IONDELIM_delim, ATEMP); |
| 199 | iop->ioflag |= IOEVAL | IONDELIM; |
| 200 | } else |
| 201 | musthave(LWORD, ishere ? HEREDELIM : 0); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 202 | if (ishere) { |
| 203 | iop->delim = yylval.cp; |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 204 | if (*ident != 0 && !(iop->ioflag & IOHERESTR)) { |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 205 | /* unquoted */ |
Elliott Hughes | b27ce95 | 2015-04-21 13:39:18 -0700 | [diff] [blame] | 206 | iop->ioflag |= IOEVAL; |
Elliott Hughes | 5001206 | 2015-03-10 22:22:24 -0700 | [diff] [blame] | 207 | } |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 208 | if (herep > &heres[HERES - 1]) |
Elliott Hughes | 77740fc | 2016-08-12 15:06:53 -0700 | [diff] [blame] | 209 | yyerror(Tf_toomany, "<<"); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 210 | *herep++ = iop; |
| 211 | } else |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 212 | iop->ioname = yylval.cp; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 213 | |
Elliott Hughes | b27ce95 | 2015-04-21 13:39:18 -0700 | [diff] [blame] | 214 | if (iop->ioflag & IOBASH) { |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 215 | char *cp; |
| 216 | |
| 217 | nextiop = alloc(sizeof(*iop), ATEMP); |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 218 | nextiop->ioname = cp = alloc(3, ATEMP); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 219 | *cp++ = CHAR; |
Elliott Hughes | 96b4363 | 2015-07-17 11:39:41 -0700 | [diff] [blame] | 220 | *cp++ = digits_lc[iop->unit % 10]; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 221 | *cp = EOS; |
| 222 | |
Elliott Hughes | b27ce95 | 2015-04-21 13:39:18 -0700 | [diff] [blame] | 223 | iop->ioflag &= ~IOBASH; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 224 | nextiop->unit = 2; |
Elliott Hughes | b27ce95 | 2015-04-21 13:39:18 -0700 | [diff] [blame] | 225 | nextiop->ioflag = IODUP; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 226 | nextiop->delim = NULL; |
| 227 | nextiop->heredoc = NULL; |
| 228 | } |
| 229 | return (iop); |
| 230 | } |
| 231 | |
| 232 | static struct op * |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 233 | nested(int type, int smark, int emark, int sALIAS) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 234 | { |
| 235 | struct op *t; |
| 236 | struct nesting_state old_nesting; |
| 237 | |
| 238 | nesting_push(&old_nesting, smark); |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 239 | t = c_list(sALIAS, true); |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 240 | musthave(emark, KEYWORD|sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 241 | nesting_pop(&old_nesting); |
Thorsten Glaser | 811a575 | 2013-07-25 14:24:45 +0000 | [diff] [blame] | 242 | return (block(type, t, NULL)); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 243 | } |
| 244 | |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 245 | static const char builtin_cmd[] = { |
| 246 | QCHAR, '\\', CHAR, 'b', CHAR, 'u', CHAR, 'i', |
| 247 | CHAR, 'l', CHAR, 't', CHAR, 'i', CHAR, 'n', EOS |
| 248 | }; |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 249 | static const char let_cmd[] = { |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 250 | CHAR, 'l', CHAR, 'e', CHAR, 't', EOS |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 251 | }; |
| 252 | static const char setA_cmd0[] = { |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 253 | CHAR, 's', CHAR, 'e', CHAR, 't', EOS |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 254 | }; |
| 255 | static const char setA_cmd1[] = { |
| 256 | CHAR, '-', CHAR, 'A', EOS |
| 257 | }; |
| 258 | static const char setA_cmd2[] = { |
| 259 | CHAR, '-', CHAR, '-', EOS |
| 260 | }; |
| 261 | |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 262 | static struct op * |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 263 | get_command(int cf, int sALIAS) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 264 | { |
| 265 | struct op *t; |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 266 | int c, iopn = 0, syniocf, lno; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 267 | struct ioword *iop, **iops; |
| 268 | XPtrV args, vars; |
| 269 | struct nesting_state old_nesting; |
| 270 | |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 271 | /* NUFILE is small enough to leave this addition unchecked */ |
| 272 | iops = alloc2((NUFILE + 1), sizeof(struct ioword *), ATEMP); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 273 | XPinit(args, 16); |
| 274 | XPinit(vars, 16); |
| 275 | |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 276 | syniocf = KEYWORD|sALIAS; |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 277 | switch (c = token(cf|KEYWORD|sALIAS|CMDASN)) { |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 278 | default: |
| 279 | REJECT; |
| 280 | afree(iops, ATEMP); |
| 281 | XPfree(args); |
| 282 | XPfree(vars); |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 283 | /* empty line */ |
| 284 | return (NULL); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 285 | |
| 286 | case LWORD: |
| 287 | case REDIR: |
| 288 | REJECT; |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 289 | syniocf &= ~(KEYWORD|sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 290 | t = newtp(TCOM); |
| 291 | t->lineno = source->line; |
Elliott Hughes | 966dd55 | 2016-12-08 15:56:04 -0800 | [diff] [blame] | 292 | goto get_command_start; |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 293 | while (/* CONSTCOND */ 1) { |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 294 | bool check_decl_utility; |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 295 | |
| 296 | if (XPsize(args) == 0) { |
Elliott Hughes | 966dd55 | 2016-12-08 15:56:04 -0800 | [diff] [blame] | 297 | get_command_start: |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 298 | check_decl_utility = true; |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 299 | cf = sALIAS | CMDASN; |
| 300 | } else if (t->u.evalflags) |
| 301 | cf = CMDWORD | CMDASN; |
| 302 | else |
| 303 | cf = CMDWORD; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 304 | switch (tpeek(cf)) { |
| 305 | case REDIR: |
| 306 | while ((iop = synio(cf)) != NULL) { |
| 307 | if (iopn >= NUFILE) |
Elliott Hughes | 77740fc | 2016-08-12 15:06:53 -0700 | [diff] [blame] | 308 | yyerror(Tf_toomany, |
| 309 | Tredirection); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 310 | iops[iopn++] = iop; |
| 311 | } |
| 312 | break; |
| 313 | |
| 314 | case LWORD: |
| 315 | ACCEPT; |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 316 | if (check_decl_utility) { |
| 317 | struct tbl *tt = get_builtin(ident); |
| 318 | uint32_t flag; |
| 319 | |
| 320 | flag = tt ? tt->flag : 0; |
| 321 | if (flag & DECL_UTIL) |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 322 | t->u.evalflags = DOVACHECK; |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 323 | if (!(flag & DECL_FWDR)) |
| 324 | check_decl_utility = false; |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 325 | } |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 326 | if ((XPsize(args) == 0 || Flag(FKEYWORD)) && |
| 327 | is_wdvarassign(yylval.cp)) |
| 328 | XPput(vars, yylval.cp); |
| 329 | else |
| 330 | XPput(args, yylval.cp); |
| 331 | break; |
| 332 | |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 333 | case '(' /*)*/: |
| 334 | if (XPsize(args) == 0 && XPsize(vars) == 1 && |
| 335 | is_wdvarassign(yylval.cp)) { |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 336 | char *tcp; |
| 337 | |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 338 | /* wdarrassign: foo=(bar) */ |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 339 | ACCEPT; |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 340 | |
| 341 | /* manipulate the vars string */ |
| 342 | tcp = XPptrv(vars)[(vars.len = 0)]; |
| 343 | /* 'varname=' -> 'varname' */ |
| 344 | tcp[wdscan(tcp, EOS) - tcp - 3] = EOS; |
| 345 | |
| 346 | /* construct new args strings */ |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 347 | XPput(args, wdcopy(builtin_cmd, ATEMP)); |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 348 | XPput(args, wdcopy(setA_cmd0, ATEMP)); |
| 349 | XPput(args, wdcopy(setA_cmd1, ATEMP)); |
| 350 | XPput(args, tcp); |
| 351 | XPput(args, wdcopy(setA_cmd2, ATEMP)); |
| 352 | |
| 353 | /* slurp in words till closing paren */ |
| 354 | while (token(CONTIN) == LWORD) |
| 355 | XPput(args, yylval.cp); |
| 356 | if (symbol != /*(*/ ')') |
| 357 | syntaxerr(NULL); |
| 358 | } else { |
| 359 | /* |
| 360 | * Check for "> foo (echo hi)" |
| 361 | * which AT&T ksh allows (not |
| 362 | * POSIX, but not disallowed) |
| 363 | */ |
| 364 | afree(t, ATEMP); |
| 365 | if (XPsize(args) == 0 && |
| 366 | XPsize(vars) == 0) { |
| 367 | ACCEPT; |
| 368 | goto Subshell; |
| 369 | } |
| 370 | |
| 371 | /* must be a function */ |
| 372 | if (iopn != 0 || XPsize(args) != 1 || |
| 373 | XPsize(vars) != 0) |
| 374 | syntaxerr(NULL); |
| 375 | ACCEPT; |
| 376 | musthave(/*(*/')', 0); |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 377 | t = function_body(XPptrv(args)[0], |
| 378 | sALIAS, false); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 379 | } |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 380 | goto Leave; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 381 | |
| 382 | default: |
| 383 | goto Leave; |
| 384 | } |
| 385 | } |
| 386 | Leave: |
| 387 | break; |
| 388 | |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 389 | case '(': /*)*/ { |
| 390 | int subshell_nesting_type_saved; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 391 | Subshell: |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 392 | subshell_nesting_type_saved = subshell_nesting_type; |
| 393 | subshell_nesting_type = ')'; |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 394 | t = nested(TPAREN, '(', ')', sALIAS); |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 395 | subshell_nesting_type = subshell_nesting_type_saved; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 396 | break; |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 397 | } |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 398 | |
| 399 | case '{': /*}*/ |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 400 | t = nested(TBRACE, '{', '}', sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 401 | break; |
| 402 | |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 403 | case MDPAREN: |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 404 | /* leave KEYWORD in syniocf (allow if (( 1 )) then ...) */ |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 405 | lno = source->line; |
| 406 | ACCEPT; |
| 407 | switch (token(LETEXPR)) { |
| 408 | case LWORD: |
| 409 | break; |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 410 | case '(': /*)*/ |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 411 | c = '('; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 412 | goto Subshell; |
| 413 | default: |
| 414 | syntaxerr(NULL); |
| 415 | } |
| 416 | t = newtp(TCOM); |
| 417 | t->lineno = lno; |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 418 | XPput(args, wdcopy(builtin_cmd, ATEMP)); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 419 | XPput(args, wdcopy(let_cmd, ATEMP)); |
| 420 | XPput(args, yylval.cp); |
| 421 | break; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 422 | |
| 423 | case DBRACKET: /* [[ .. ]] */ |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 424 | /* leave KEYWORD in syniocf (allow if [[ -n 1 ]] then ...) */ |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 425 | t = newtp(TDBRACKET); |
| 426 | ACCEPT; |
| 427 | { |
| 428 | Test_env te; |
| 429 | |
| 430 | te.flags = TEF_DBRACKET; |
| 431 | te.pos.av = &args; |
| 432 | te.isa = dbtestp_isa; |
| 433 | te.getopnd = dbtestp_getopnd; |
| 434 | te.eval = dbtestp_eval; |
| 435 | te.error = dbtestp_error; |
| 436 | |
| 437 | test_parse(&te); |
| 438 | } |
| 439 | break; |
| 440 | |
| 441 | case FOR: |
| 442 | case SELECT: |
| 443 | t = newtp((c == FOR) ? TFOR : TSELECT); |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 444 | musthave(LWORD, CMDASN); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 445 | if (!is_wdvarname(yylval.cp, true)) |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 446 | yyerror("%s: bad identifier", |
Elliott Hughes | 77740fc | 2016-08-12 15:06:53 -0700 | [diff] [blame] | 447 | c == FOR ? "for" : Tselect); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 448 | strdupx(t->str, ident, ATEMP); |
| 449 | nesting_push(&old_nesting, c); |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 450 | t->vars = wordlist(sALIAS); |
| 451 | t->left = dogroup(sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 452 | nesting_pop(&old_nesting); |
| 453 | break; |
| 454 | |
| 455 | case WHILE: |
| 456 | case UNTIL: |
| 457 | nesting_push(&old_nesting, c); |
| 458 | t = newtp((c == WHILE) ? TWHILE : TUNTIL); |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 459 | t->left = c_list(sALIAS, true); |
| 460 | t->right = dogroup(sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 461 | nesting_pop(&old_nesting); |
| 462 | break; |
| 463 | |
| 464 | case CASE: |
| 465 | t = newtp(TCASE); |
| 466 | musthave(LWORD, 0); |
| 467 | t->str = yylval.cp; |
| 468 | nesting_push(&old_nesting, c); |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 469 | t->left = caselist(sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 470 | nesting_pop(&old_nesting); |
| 471 | break; |
| 472 | |
| 473 | case IF: |
| 474 | nesting_push(&old_nesting, c); |
| 475 | t = newtp(TIF); |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 476 | t->left = c_list(sALIAS, true); |
| 477 | t->right = thenpart(sALIAS); |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 478 | musthave(FI, KEYWORD|sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 479 | nesting_pop(&old_nesting); |
| 480 | break; |
| 481 | |
| 482 | case BANG: |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 483 | syniocf &= ~(KEYWORD|sALIAS); |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 484 | t = pipeline(0, sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 485 | if (t == NULL) |
| 486 | syntaxerr(NULL); |
Thorsten Glaser | 811a575 | 2013-07-25 14:24:45 +0000 | [diff] [blame] | 487 | t = block(TBANG, NULL, t); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 488 | break; |
| 489 | |
| 490 | case TIME: |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 491 | syniocf &= ~(KEYWORD|sALIAS); |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 492 | t = pipeline(0, sALIAS); |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 493 | if (t && t->type == TCOM) { |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 494 | t->str = alloc(2, ATEMP); |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 495 | /* TF_* flags */ |
| 496 | t->str[0] = '\0'; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 497 | t->str[1] = '\0'; |
| 498 | } |
Thorsten Glaser | 811a575 | 2013-07-25 14:24:45 +0000 | [diff] [blame] | 499 | t = block(TTIME, t, NULL); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 500 | break; |
| 501 | |
| 502 | case FUNCTION: |
| 503 | musthave(LWORD, 0); |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 504 | t = function_body(yylval.cp, sALIAS, true); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 505 | break; |
| 506 | } |
| 507 | |
| 508 | while ((iop = synio(syniocf)) != NULL) { |
| 509 | if (iopn >= NUFILE) |
Elliott Hughes | 77740fc | 2016-08-12 15:06:53 -0700 | [diff] [blame] | 510 | yyerror(Tf_toomany, Tredirection); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 511 | iops[iopn++] = iop; |
| 512 | } |
| 513 | |
| 514 | if (iopn == 0) { |
| 515 | afree(iops, ATEMP); |
| 516 | t->ioact = NULL; |
| 517 | } else { |
| 518 | iops[iopn++] = NULL; |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 519 | iops = aresize2(iops, iopn, sizeof(struct ioword *), ATEMP); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 520 | t->ioact = iops; |
| 521 | } |
| 522 | |
| 523 | if (t->type == TCOM || t->type == TDBRACKET) { |
| 524 | XPput(args, NULL); |
| 525 | t->args = (const char **)XPclose(args); |
| 526 | XPput(vars, NULL); |
Thorsten Glaser | 811a575 | 2013-07-25 14:24:45 +0000 | [diff] [blame] | 527 | t->vars = (char **)XPclose(vars); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 528 | } else { |
| 529 | XPfree(args); |
| 530 | XPfree(vars); |
| 531 | } |
| 532 | |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 533 | if (c == MDPAREN) { |
| 534 | t = block(TBRACE, t, NULL); |
| 535 | t->ioact = t->left->ioact; |
| 536 | t->left->ioact = NULL; |
| 537 | } |
| 538 | |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 539 | return (t); |
| 540 | } |
| 541 | |
| 542 | static struct op * |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 543 | dogroup(int sALIAS) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 544 | { |
| 545 | int c; |
| 546 | struct op *list; |
| 547 | |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 548 | c = token(CONTIN|KEYWORD|sALIAS); |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 549 | /* |
| 550 | * A {...} can be used instead of do...done for for/select loops |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 551 | * but not for while/until loops - we don't need to check if it |
| 552 | * is a while loop because it would have been parsed as part of |
| 553 | * the conditional command list... |
| 554 | */ |
| 555 | if (c == DO) |
| 556 | c = DONE; |
| 557 | else if (c == '{') |
| 558 | c = '}'; |
| 559 | else |
| 560 | syntaxerr(NULL); |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 561 | list = c_list(sALIAS, true); |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 562 | musthave(c, KEYWORD|sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 563 | return (list); |
| 564 | } |
| 565 | |
| 566 | static struct op * |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 567 | thenpart(int sALIAS) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 568 | { |
| 569 | struct op *t; |
| 570 | |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 571 | musthave(THEN, KEYWORD|sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 572 | t = newtp(0); |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 573 | t->left = c_list(sALIAS, true); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 574 | if (t->left == NULL) |
| 575 | syntaxerr(NULL); |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 576 | t->right = elsepart(sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 577 | return (t); |
| 578 | } |
| 579 | |
| 580 | static struct op * |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 581 | elsepart(int sALIAS) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 582 | { |
| 583 | struct op *t; |
| 584 | |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 585 | switch (token(KEYWORD|sALIAS|CMDASN)) { |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 586 | case ELSE: |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 587 | if ((t = c_list(sALIAS, true)) == NULL) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 588 | syntaxerr(NULL); |
| 589 | return (t); |
| 590 | |
| 591 | case ELIF: |
| 592 | t = newtp(TELIF); |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 593 | t->left = c_list(sALIAS, true); |
| 594 | t->right = thenpart(sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 595 | return (t); |
| 596 | |
| 597 | default: |
| 598 | REJECT; |
| 599 | } |
| 600 | return (NULL); |
| 601 | } |
| 602 | |
| 603 | static struct op * |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 604 | caselist(int sALIAS) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 605 | { |
| 606 | struct op *t, *tl; |
| 607 | int c; |
| 608 | |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 609 | c = token(CONTIN|KEYWORD|sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 610 | /* A {...} can be used instead of in...esac for case statements */ |
| 611 | if (c == IN) |
| 612 | c = ESAC; |
| 613 | else if (c == '{') |
| 614 | c = '}'; |
| 615 | else |
| 616 | syntaxerr(NULL); |
| 617 | t = tl = NULL; |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 618 | /* no ALIAS here */ |
| 619 | while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != c) { |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 620 | struct op *tc = casepart(c, sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 621 | if (tl == NULL) |
| 622 | t = tl = tc, tl->right = NULL; |
| 623 | else |
| 624 | tl->right = tc, tl = tc; |
| 625 | } |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 626 | musthave(c, KEYWORD|sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 627 | return (t); |
| 628 | } |
| 629 | |
| 630 | static struct op * |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 631 | casepart(int endtok, int sALIAS) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 632 | { |
| 633 | struct op *t; |
| 634 | XPtrV ptns; |
| 635 | |
| 636 | XPinit(ptns, 16); |
| 637 | t = newtp(TPAT); |
| 638 | /* no ALIAS here */ |
| 639 | if (token(CONTIN | KEYWORD) != '(') |
| 640 | REJECT; |
| 641 | do { |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 642 | switch (token(0)) { |
| 643 | case LWORD: |
| 644 | break; |
| 645 | case '}': |
| 646 | case ESAC: |
| 647 | if (symbol != endtok) { |
| 648 | strdupx(yylval.cp, |
| 649 | symbol == '}' ? Tcbrace : Tesac, ATEMP); |
| 650 | break; |
| 651 | } |
| 652 | /* FALLTHROUGH */ |
| 653 | default: |
| 654 | syntaxerr(NULL); |
| 655 | } |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 656 | XPput(ptns, yylval.cp); |
| 657 | } while (token(0) == '|'); |
| 658 | REJECT; |
| 659 | XPput(ptns, NULL); |
Thorsten Glaser | 811a575 | 2013-07-25 14:24:45 +0000 | [diff] [blame] | 660 | t->vars = (char **)XPclose(ptns); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 661 | musthave(')', 0); |
| 662 | |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 663 | t->left = c_list(sALIAS, true); |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 664 | |
| 665 | /* initialise to default for ;; or omitted */ |
| 666 | t->u.charflag = ';'; |
| 667 | /* SUSv4 requires the ;; except in the last casepart */ |
| 668 | if ((tpeek(CONTIN|KEYWORD|sALIAS)) != endtok) |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 669 | switch (symbol) { |
| 670 | default: |
| 671 | syntaxerr(NULL); |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 672 | case BRKEV: |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 673 | t->u.charflag = '|'; |
| 674 | if (0) |
| 675 | /* FALLTHROUGH */ |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 676 | case BRKFT: |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 677 | t->u.charflag = '&'; |
| 678 | /* FALLTHROUGH */ |
| 679 | case BREAK: |
| 680 | /* initialised above, but we need to eat the token */ |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 681 | ACCEPT; |
| 682 | } |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 683 | return (t); |
| 684 | } |
| 685 | |
| 686 | static struct op * |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 687 | function_body(char *name, int sALIAS, |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 688 | /* function foo { ... } vs foo() { .. } */ |
| 689 | bool ksh_func) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 690 | { |
| 691 | char *sname, *p; |
| 692 | struct op *t; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 693 | |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 694 | sname = wdstrip(name, 0); |
| 695 | /*- |
| 696 | * Check for valid characters in name. POSIX and AT&T ksh93 say |
| 697 | * only allow [a-zA-Z_0-9] but this allows more as old pdkshs |
| 698 | * have allowed more; the following were never allowed: |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 699 | * NUL TAB NL SP " $ & ' ( ) ; < = > \ ` | |
| 700 | * C_QUOTE covers all but adds # * ? [ ] |
| 701 | */ |
| 702 | for (p = sname; *p; p++) |
| 703 | if (ctype(*p, C_QUOTE)) |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 704 | yyerror(Tinvname, sname, Tfunction); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 705 | |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 706 | /* |
| 707 | * Note that POSIX allows only compound statements after foo(), |
| 708 | * sh and AT&T ksh allow any command, go with the later since it |
| 709 | * shouldn't break anything. However, for function foo, AT&T ksh |
| 710 | * only accepts an open-brace. |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 711 | */ |
| 712 | if (ksh_func) { |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 713 | if (tpeek(CONTIN|KEYWORD|sALIAS) == '(' /*)*/) { |
| 714 | /* function foo () { //}*/ |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 715 | ACCEPT; |
| 716 | musthave(')', 0); |
| 717 | /* degrade to POSIX function */ |
| 718 | ksh_func = false; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 719 | } |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 720 | musthave('{' /*}*/, CONTIN|KEYWORD|sALIAS); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 721 | REJECT; |
| 722 | } |
| 723 | |
| 724 | t = newtp(TFUNCT); |
| 725 | t->str = sname; |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 726 | t->u.ksh_func = tobool(ksh_func); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 727 | t->lineno = source->line; |
| 728 | |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 729 | if ((t->left = get_command(CONTIN, sALIAS)) == NULL) { |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 730 | char *tv; |
| 731 | /* |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 732 | * Probably something like foo() followed by EOF or ';'. |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 733 | * This is accepted by sh and ksh88. |
| 734 | * To make "typeset -f foo" work reliably (so its output can |
| 735 | * be used as input), we pretend there is a colon here. |
| 736 | */ |
| 737 | t->left = newtp(TCOM); |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 738 | /* (2 * sizeof(char *)) is small enough */ |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 739 | t->left->args = alloc(2 * sizeof(char *), ATEMP); |
| 740 | t->left->args[0] = tv = alloc(3, ATEMP); |
Elliott Hughes | 96b4363 | 2015-07-17 11:39:41 -0700 | [diff] [blame] | 741 | tv[0] = QCHAR; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 742 | tv[1] = ':'; |
| 743 | tv[2] = EOS; |
| 744 | t->left->args[1] = NULL; |
| 745 | t->left->vars = alloc(sizeof(char *), ATEMP); |
| 746 | t->left->vars[0] = NULL; |
| 747 | t->left->lineno = 1; |
| 748 | } |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 749 | |
| 750 | return (t); |
| 751 | } |
| 752 | |
| 753 | static char ** |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 754 | wordlist(int sALIAS) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 755 | { |
| 756 | int c; |
| 757 | XPtrV args; |
| 758 | |
| 759 | XPinit(args, 16); |
| 760 | /* POSIX does not do alias expansion here... */ |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 761 | if ((c = token(CONTIN|KEYWORD|sALIAS)) != IN) { |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 762 | if (c != ';') |
| 763 | /* non-POSIX, but AT&T ksh accepts a ; here */ |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 764 | REJECT; |
| 765 | return (NULL); |
| 766 | } |
| 767 | while ((c = token(0)) == LWORD) |
| 768 | XPput(args, yylval.cp); |
| 769 | if (c != '\n' && c != ';') |
| 770 | syntaxerr(NULL); |
Thorsten Glaser | 811a575 | 2013-07-25 14:24:45 +0000 | [diff] [blame] | 771 | XPput(args, NULL); |
| 772 | return ((char **)XPclose(args)); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 773 | } |
| 774 | |
| 775 | /* |
| 776 | * supporting functions |
| 777 | */ |
| 778 | |
| 779 | static struct op * |
Thorsten Glaser | 811a575 | 2013-07-25 14:24:45 +0000 | [diff] [blame] | 780 | block(int type, struct op *t1, struct op *t2) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 781 | { |
| 782 | struct op *t; |
| 783 | |
| 784 | t = newtp(type); |
| 785 | t->left = t1; |
| 786 | t->right = t2; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 787 | return (t); |
| 788 | } |
| 789 | |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 790 | static const struct tokeninfo { |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 791 | const char *name; |
| 792 | short val; |
| 793 | short reserved; |
| 794 | } tokentab[] = { |
| 795 | /* Reserved words */ |
| 796 | { "if", IF, true }, |
| 797 | { "then", THEN, true }, |
| 798 | { "else", ELSE, true }, |
| 799 | { "elif", ELIF, true }, |
| 800 | { "fi", FI, true }, |
| 801 | { "case", CASE, true }, |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 802 | { Tesac, ESAC, true }, |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 803 | { "for", FOR, true }, |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 804 | { Tselect, SELECT, true }, |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 805 | { "while", WHILE, true }, |
| 806 | { "until", UNTIL, true }, |
| 807 | { "do", DO, true }, |
| 808 | { "done", DONE, true }, |
| 809 | { "in", IN, true }, |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 810 | { Tfunction, FUNCTION, true }, |
Elliott Hughes | 77740fc | 2016-08-12 15:06:53 -0700 | [diff] [blame] | 811 | { Ttime, TIME, true }, |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 812 | { "{", '{', true }, |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 813 | { Tcbrace, '}', true }, |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 814 | { "!", BANG, true }, |
| 815 | { "[[", DBRACKET, true }, |
| 816 | /* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */ |
| 817 | { "&&", LOGAND, false }, |
| 818 | { "||", LOGOR, false }, |
| 819 | { ";;", BREAK, false }, |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 820 | { ";|", BRKEV, false }, |
| 821 | { ";&", BRKFT, false }, |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 822 | { "((", MDPAREN, false }, |
| 823 | { "|&", COPROC, false }, |
| 824 | /* and some special cases... */ |
| 825 | { "newline", '\n', false }, |
| 826 | { NULL, 0, false } |
| 827 | }; |
| 828 | |
| 829 | void |
| 830 | initkeywords(void) |
| 831 | { |
| 832 | struct tokeninfo const *tt; |
| 833 | struct tbl *p; |
| 834 | |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 835 | ktinit(APERM, &keywords, |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 836 | /* currently 28 keywords: 75% of 64 = 2^6 */ |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 837 | 6); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 838 | for (tt = tokentab; tt->name; tt++) { |
| 839 | if (tt->reserved) { |
| 840 | p = ktenter(&keywords, tt->name, hash(tt->name)); |
| 841 | p->flag |= DEFINED|ISSET; |
| 842 | p->type = CKEYWD; |
| 843 | p->val.i = tt->val; |
| 844 | } |
| 845 | } |
| 846 | } |
| 847 | |
| 848 | static void |
| 849 | syntaxerr(const char *what) |
| 850 | { |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 851 | /* 23<<- is the longest redirection, I think */ |
| 852 | char redir[8]; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 853 | const char *s; |
| 854 | struct tokeninfo const *tt; |
| 855 | int c; |
| 856 | |
| 857 | if (!what) |
Elliott Hughes | 77740fc | 2016-08-12 15:06:53 -0700 | [diff] [blame] | 858 | what = Tunexpected; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 859 | REJECT; |
| 860 | c = token(0); |
| 861 | Again: |
| 862 | switch (c) { |
| 863 | case 0: |
| 864 | if (nesting.start_token) { |
| 865 | c = nesting.start_token; |
| 866 | source->errline = nesting.start_line; |
| 867 | what = "unmatched"; |
| 868 | goto Again; |
| 869 | } |
| 870 | /* don't quote the EOF */ |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 871 | yyerror("%s: unexpected EOF", Tsynerr); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 872 | /* NOTREACHED */ |
| 873 | |
| 874 | case LWORD: |
Elliott Hughes | 77740fc | 2016-08-12 15:06:53 -0700 | [diff] [blame] | 875 | s = snptreef(NULL, 32, Tf_S, yylval.cp); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 876 | break; |
| 877 | |
| 878 | case REDIR: |
Elliott Hughes | 77740fc | 2016-08-12 15:06:53 -0700 | [diff] [blame] | 879 | s = snptreef(redir, sizeof(redir), Tft_R, yylval.iop); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 880 | break; |
| 881 | |
| 882 | default: |
| 883 | for (tt = tokentab; tt->name; tt++) |
| 884 | if (tt->val == c) |
| 885 | break; |
| 886 | if (tt->name) |
| 887 | s = tt->name; |
| 888 | else { |
| 889 | if (c > 0 && c < 256) { |
| 890 | redir[0] = c; |
| 891 | redir[1] = '\0'; |
| 892 | } else |
| 893 | shf_snprintf(redir, sizeof(redir), |
| 894 | "?%d", c); |
| 895 | s = redir; |
| 896 | } |
| 897 | } |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 898 | yyerror(Tf_sD_s_qs, Tsynerr, what, s); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 899 | } |
| 900 | |
| 901 | static void |
| 902 | nesting_push(struct nesting_state *save, int tok) |
| 903 | { |
| 904 | *save = nesting; |
| 905 | nesting.start_token = tok; |
| 906 | nesting.start_line = source->line; |
| 907 | } |
| 908 | |
| 909 | static void |
| 910 | nesting_pop(struct nesting_state *saved) |
| 911 | { |
| 912 | nesting = *saved; |
| 913 | } |
| 914 | |
| 915 | static struct op * |
| 916 | newtp(int type) |
| 917 | { |
| 918 | struct op *t; |
| 919 | |
| 920 | t = alloc(sizeof(struct op), ATEMP); |
| 921 | t->type = type; |
| 922 | t->u.evalflags = 0; |
| 923 | t->args = NULL; |
| 924 | t->vars = NULL; |
| 925 | t->ioact = NULL; |
| 926 | t->left = t->right = NULL; |
| 927 | t->str = NULL; |
| 928 | return (t); |
| 929 | } |
| 930 | |
| 931 | struct op * |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 932 | compile(Source *s, bool skiputf8bom, bool doalias) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 933 | { |
| 934 | nesting.start_token = 0; |
| 935 | nesting.start_line = 0; |
| 936 | herep = heres; |
| 937 | source = s; |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 938 | if (skiputf8bom) |
| 939 | yyskiputf8bom(); |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 940 | yyparse(doalias); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 941 | return (outtree); |
| 942 | } |
| 943 | |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 944 | /* Check if we are in the middle of reading an alias */ |
| 945 | static int |
| 946 | inalias(struct source *s) |
| 947 | { |
Elliott Hughes | 77740fc | 2016-08-12 15:06:53 -0700 | [diff] [blame] | 948 | while (s && s->type == SALIAS) { |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 949 | if (!(s->flags & SF_ALIASEND)) |
| 950 | return (1); |
Elliott Hughes | 77740fc | 2016-08-12 15:06:53 -0700 | [diff] [blame] | 951 | s = s->next; |
| 952 | } |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 953 | return (0); |
| 954 | } |
| 955 | |
| 956 | |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 957 | /* |
| 958 | * Order important - indexed by Test_meta values |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 959 | * Note that ||, &&, ( and ) can't appear in as unquoted strings |
| 960 | * in normal shell input, so these can be interpreted unambiguously |
| 961 | * in the evaluation pass. |
| 962 | */ |
| 963 | static const char dbtest_or[] = { CHAR, '|', CHAR, '|', EOS }; |
| 964 | static const char dbtest_and[] = { CHAR, '&', CHAR, '&', EOS }; |
| 965 | static const char dbtest_not[] = { CHAR, '!', EOS }; |
| 966 | static const char dbtest_oparen[] = { CHAR, '(', EOS }; |
| 967 | static const char dbtest_cparen[] = { CHAR, ')', EOS }; |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 968 | const char * const dbtest_tokens[] = { |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 969 | dbtest_or, dbtest_and, dbtest_not, |
| 970 | dbtest_oparen, dbtest_cparen |
| 971 | }; |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 972 | static const char db_close[] = { CHAR, ']', CHAR, ']', EOS }; |
| 973 | static const char db_lthan[] = { CHAR, '<', EOS }; |
| 974 | static const char db_gthan[] = { CHAR, '>', EOS }; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 975 | |
| 976 | /* |
| 977 | * Test if the current token is a whatever. Accepts the current token if |
| 978 | * it is. Returns 0 if it is not, non-zero if it is (in the case of |
| 979 | * TM_UNOP and TM_BINOP, the returned value is a Test_op). |
| 980 | */ |
| 981 | static Test_op |
| 982 | dbtestp_isa(Test_env *te, Test_meta meta) |
| 983 | { |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 984 | int c = tpeek(CMDASN | (meta == TM_BINOP ? 0 : CONTIN)); |
Elliott Hughes | 737fdce | 2014-08-07 12:59:26 -0700 | [diff] [blame] | 985 | bool uqword; |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 986 | char *save = NULL; |
| 987 | Test_op ret = TO_NONOP; |
| 988 | |
| 989 | /* unquoted word? */ |
| 990 | uqword = c == LWORD && *ident; |
| 991 | |
| 992 | if (meta == TM_OR) |
| 993 | ret = c == LOGOR ? TO_NONNULL : TO_NONOP; |
| 994 | else if (meta == TM_AND) |
| 995 | ret = c == LOGAND ? TO_NONNULL : TO_NONOP; |
| 996 | else if (meta == TM_NOT) |
| 997 | ret = (uqword && !strcmp(yylval.cp, |
| 998 | dbtest_tokens[(int)TM_NOT])) ? TO_NONNULL : TO_NONOP; |
| 999 | else if (meta == TM_OPAREN) |
| 1000 | ret = c == '(' /*)*/ ? TO_NONNULL : TO_NONOP; |
| 1001 | else if (meta == TM_CPAREN) |
| 1002 | ret = c == /*(*/ ')' ? TO_NONNULL : TO_NONOP; |
| 1003 | else if (meta == TM_UNOP || meta == TM_BINOP) { |
| 1004 | if (meta == TM_BINOP && c == REDIR && |
Elliott Hughes | b27ce95 | 2015-04-21 13:39:18 -0700 | [diff] [blame] | 1005 | (yylval.iop->ioflag == IOREAD || |
| 1006 | yylval.iop->ioflag == IOWRITE)) { |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 1007 | ret = TO_NONNULL; |
Elliott Hughes | b27ce95 | 2015-04-21 13:39:18 -0700 | [diff] [blame] | 1008 | save = wdcopy(yylval.iop->ioflag == IOREAD ? |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 1009 | db_lthan : db_gthan, ATEMP); |
| 1010 | } else if (uqword && (ret = test_isop(meta, ident))) |
| 1011 | save = yylval.cp; |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 1012 | } else |
| 1013 | /* meta == TM_END */ |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 1014 | ret = (uqword && !strcmp(yylval.cp, |
| 1015 | db_close)) ? TO_NONNULL : TO_NONOP; |
| 1016 | if (ret != TO_NONOP) { |
| 1017 | ACCEPT; |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 1018 | if ((unsigned int)meta < NELEM(dbtest_tokens)) |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 1019 | save = wdcopy(dbtest_tokens[(int)meta], ATEMP); |
| 1020 | if (save) |
| 1021 | XPput(*te->pos.av, save); |
| 1022 | } |
| 1023 | return (ret); |
| 1024 | } |
| 1025 | |
| 1026 | static const char * |
| 1027 | dbtestp_getopnd(Test_env *te, Test_op op MKSH_A_UNUSED, |
| 1028 | bool do_eval MKSH_A_UNUSED) |
| 1029 | { |
Elliott Hughes | fc0307d | 2016-02-02 15:26:47 -0800 | [diff] [blame] | 1030 | int c = tpeek(CMDASN); |
Jean-Baptiste Queru | 5155f1c | 2011-06-16 10:05:28 -0700 | [diff] [blame] | 1031 | |
| 1032 | if (c != LWORD) |
| 1033 | return (NULL); |
| 1034 | |
| 1035 | ACCEPT; |
| 1036 | XPput(*te->pos.av, yylval.cp); |
| 1037 | |
| 1038 | return (null); |
| 1039 | } |
| 1040 | |
| 1041 | static int |
| 1042 | dbtestp_eval(Test_env *te MKSH_A_UNUSED, Test_op op MKSH_A_UNUSED, |
| 1043 | const char *opnd1 MKSH_A_UNUSED, const char *opnd2 MKSH_A_UNUSED, |
| 1044 | bool do_eval MKSH_A_UNUSED) |
| 1045 | { |
| 1046 | return (1); |
| 1047 | } |
| 1048 | |
| 1049 | static void |
| 1050 | dbtestp_error(Test_env *te, int offset, const char *msg) |
| 1051 | { |
| 1052 | te->flags |= TEF_ERROR; |
| 1053 | |
| 1054 | if (offset < 0) { |
| 1055 | REJECT; |
| 1056 | /* Kludgy to say the least... */ |
| 1057 | symbol = LWORD; |
| 1058 | yylval.cp = *(XPptrv(*te->pos.av) + XPsize(*te->pos.av) + |
| 1059 | offset); |
| 1060 | } |
| 1061 | syntaxerr(msg); |
| 1062 | } |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 1063 | |
| 1064 | #if HAVE_SELECT |
| 1065 | |
| 1066 | #ifndef EOVERFLOW |
| 1067 | #ifdef ERANGE |
| 1068 | #define EOVERFLOW ERANGE |
| 1069 | #else |
| 1070 | #define EOVERFLOW EINVAL |
| 1071 | #endif |
| 1072 | #endif |
| 1073 | |
| 1074 | bool |
| 1075 | parse_usec(const char *s, struct timeval *tv) |
| 1076 | { |
| 1077 | struct timeval tt; |
| 1078 | int i; |
| 1079 | |
| 1080 | tv->tv_sec = 0; |
| 1081 | /* parse integral part */ |
| 1082 | while (ksh_isdigit(*s)) { |
Elliott Hughes | 96b4363 | 2015-07-17 11:39:41 -0700 | [diff] [blame] | 1083 | tt.tv_sec = tv->tv_sec * 10 + ksh_numdig(*s++); |
| 1084 | /*XXX this overflow check maybe UB */ |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 1085 | if (tt.tv_sec / 10 != tv->tv_sec) { |
| 1086 | errno = EOVERFLOW; |
| 1087 | return (true); |
| 1088 | } |
| 1089 | tv->tv_sec = tt.tv_sec; |
| 1090 | } |
| 1091 | |
| 1092 | tv->tv_usec = 0; |
| 1093 | if (!*s) |
| 1094 | /* no decimal fraction */ |
| 1095 | return (false); |
| 1096 | else if (*s++ != '.') { |
| 1097 | /* junk after integral part */ |
| 1098 | errno = EINVAL; |
| 1099 | return (true); |
| 1100 | } |
| 1101 | |
| 1102 | /* parse decimal fraction */ |
| 1103 | i = 100000; |
| 1104 | while (ksh_isdigit(*s)) { |
Elliott Hughes | 96b4363 | 2015-07-17 11:39:41 -0700 | [diff] [blame] | 1105 | tv->tv_usec += i * ksh_numdig(*s++); |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 1106 | if (i == 1) |
| 1107 | break; |
| 1108 | i /= 10; |
| 1109 | } |
| 1110 | /* check for junk after fractional part */ |
| 1111 | while (ksh_isdigit(*s)) |
| 1112 | ++s; |
| 1113 | if (*s) { |
| 1114 | errno = EINVAL; |
| 1115 | return (true); |
| 1116 | } |
| 1117 | |
| 1118 | /* end of input string reached, no errors */ |
| 1119 | return (false); |
| 1120 | } |
| 1121 | #endif |
| 1122 | |
| 1123 | /* |
| 1124 | * Helper function called from within lex.c:yylex() to parse |
| 1125 | * a COMSUB recursively using the main shell parser and lexer |
| 1126 | */ |
| 1127 | char * |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 1128 | yyrecursive(int subtype) |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 1129 | { |
| 1130 | struct op *t; |
| 1131 | char *cp; |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 1132 | struct yyrecursive_state *ys; |
| 1133 | int stok, etok; |
| 1134 | |
Thorsten Glaser | 811a575 | 2013-07-25 14:24:45 +0000 | [diff] [blame] | 1135 | if (subtype != COMSUB) { |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 1136 | stok = '{'; |
| 1137 | etok = '}'; |
| 1138 | } else { |
| 1139 | stok = '('; |
| 1140 | etok = ')'; |
| 1141 | } |
| 1142 | |
| 1143 | ys = alloc(sizeof(struct yyrecursive_state), ATEMP); |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 1144 | |
| 1145 | /* tell the lexer to accept a closing parenthesis as EOD */ |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 1146 | ys->old_nesting_type = subshell_nesting_type; |
| 1147 | subshell_nesting_type = etok; |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 1148 | |
| 1149 | /* push reject state, parse recursively, pop reject state */ |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 1150 | ys->old_reject = reject; |
| 1151 | ys->old_symbol = symbol; |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 1152 | ACCEPT; |
Elliott Hughes | 77740fc | 2016-08-12 15:06:53 -0700 | [diff] [blame] | 1153 | memcpy(ys->old_heres, heres, sizeof(heres)); |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 1154 | ys->old_herep = herep; |
Elliott Hughes | 77740fc | 2016-08-12 15:06:53 -0700 | [diff] [blame] | 1155 | herep = heres; |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 1156 | ys->next = e->yyrecursive_statep; |
| 1157 | e->yyrecursive_statep = ys; |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 1158 | /* we use TPAREN as a helper container here */ |
Elliott Hughes | a3c3f96 | 2017-04-12 16:52:30 -0700 | [diff] [blame] | 1159 | t = nested(TPAREN, stok, etok, ALIAS); |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 1160 | yyrecursive_pop(false); |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 1161 | |
| 1162 | /* t->left because nested(TPAREN, ...) hides our goodies there */ |
Elliott Hughes | 77740fc | 2016-08-12 15:06:53 -0700 | [diff] [blame] | 1163 | cp = snptreef(NULL, 0, Tf_T, t->left); |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 1164 | tfree(t, ATEMP); |
| 1165 | |
Geremy Condra | 03ebf06 | 2011-10-12 18:17:24 -0700 | [diff] [blame] | 1166 | return (cp); |
| 1167 | } |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 1168 | |
| 1169 | void |
| 1170 | yyrecursive_pop(bool popall) |
| 1171 | { |
| 1172 | struct yyrecursive_state *ys; |
| 1173 | |
| 1174 | popnext: |
| 1175 | if (!(ys = e->yyrecursive_statep)) |
| 1176 | return; |
| 1177 | e->yyrecursive_statep = ys->next; |
| 1178 | |
Elliott Hughes | 77740fc | 2016-08-12 15:06:53 -0700 | [diff] [blame] | 1179 | memcpy(heres, ys->old_heres, sizeof(heres)); |
Thorsten Glaser | c2dc5de | 2013-02-18 23:02:51 +0000 | [diff] [blame] | 1180 | herep = ys->old_herep; |
| 1181 | reject = ys->old_reject; |
| 1182 | symbol = ys->old_symbol; |
| 1183 | |
| 1184 | subshell_nesting_type = ys->old_nesting_type; |
| 1185 | |
| 1186 | afree(ys, ATEMP); |
| 1187 | if (popall) |
| 1188 | goto popnext; |
| 1189 | } |