aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2023-05-30 16:42:18 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2023-05-30 16:44:04 +0200
commit0256e00a9d077588bd3a39f5a1ef7e2eaa2911e4 (patch)
treebdd3f15616a63a3261f5433c4df7bf83ce9f1be3
parentfe0b7985483a93d3416e0e5c9e761b6ee1ba310b (diff)
downloadbusybox-w32-0256e00a9d077588bd3a39f5a1ef7e2eaa2911e4.tar.gz
busybox-w32-0256e00a9d077588bd3a39f5a1ef7e2eaa2911e4.tar.bz2
busybox-w32-0256e00a9d077588bd3a39f5a1ef7e2eaa2911e4.zip
awk: fix precedence of = relative to ==
Discovered while adding code to disallow assignments to non-lvalues function old new delta parse_expr 936 991 +55 .rodata 105243 105247 +4 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 59/0) Total: 59 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--editors/awk.c66
-rwxr-xr-xtestsuite/awk.tests5
2 files changed, 50 insertions, 21 deletions
diff --git a/editors/awk.c b/editors/awk.c
index c49ad6e02..0f062dcdb 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -337,7 +337,9 @@ static void debug_parse_print_tc(uint32_t n)
337#undef P 337#undef P
338#undef PRIMASK 338#undef PRIMASK
339#undef PRIMASK2 339#undef PRIMASK2
340#define P(x) (x << 24) 340/* Smaller 'x' means _higher_ operator precedence */
341#define PRECEDENCE(x) (x << 24)
342#define P(x) PRECEDENCE(x)
341#define PRIMASK 0x7F000000 343#define PRIMASK 0x7F000000
342#define PRIMASK2 0x7E000000 344#define PRIMASK2 0x7E000000
343 345
@@ -360,7 +362,7 @@ enum {
360 OC_MOVE = 0x1f00, OC_PGETLINE = 0x2000, OC_REGEXP = 0x2100, 362 OC_MOVE = 0x1f00, OC_PGETLINE = 0x2000, OC_REGEXP = 0x2100,
361 OC_REPLACE = 0x2200, OC_RETURN = 0x2300, OC_SPRINTF = 0x2400, 363 OC_REPLACE = 0x2200, OC_RETURN = 0x2300, OC_SPRINTF = 0x2400,
362 OC_TERNARY = 0x2500, OC_UNARY = 0x2600, OC_VAR = 0x2700, 364 OC_TERNARY = 0x2500, OC_UNARY = 0x2600, OC_VAR = 0x2700,
363 OC_DONE = 0x2800, 365 OC_CONST = 0x2800, OC_DONE = 0x2900,
364 366
365 ST_IF = 0x3000, ST_DO = 0x3100, ST_FOR = 0x3200, 367 ST_IF = 0x3000, ST_DO = 0x3100, ST_FOR = 0x3200,
366 ST_WHILE = 0x3300 368 ST_WHILE = 0x3300
@@ -440,9 +442,9 @@ static const uint32_t tokeninfo[] ALIGN4 = {
440#define TI_PREINC (OC_UNARY|xV|P(9)|'P') 442#define TI_PREINC (OC_UNARY|xV|P(9)|'P')
441#define TI_PREDEC (OC_UNARY|xV|P(9)|'M') 443#define TI_PREDEC (OC_UNARY|xV|P(9)|'M')
442 TI_PREINC, TI_PREDEC, OC_FIELD|xV|P(5), 444 TI_PREINC, TI_PREDEC, OC_FIELD|xV|P(5),
443 OC_COMPARE|VV|P(39)|5, OC_MOVE|VV|P(74), OC_REPLACE|NV|P(74)|'+', OC_REPLACE|NV|P(74)|'-', 445 OC_COMPARE|VV|P(39)|5, OC_MOVE|VV|P(38), OC_REPLACE|NV|P(38)|'+', OC_REPLACE|NV|P(38)|'-',
444 OC_REPLACE|NV|P(74)|'*', OC_REPLACE|NV|P(74)|'/', OC_REPLACE|NV|P(74)|'%', OC_REPLACE|NV|P(74)|'&', 446 OC_REPLACE|NV|P(38)|'*', OC_REPLACE|NV|P(38)|'/', OC_REPLACE|NV|P(38)|'%', OC_REPLACE|NV|P(38)|'&',
445 OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-', OC_REPLACE|NV|P(74)|'&', OC_BINARY|NV|P(15)|'&', 447 OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-', OC_REPLACE|NV|P(38)|'&', OC_BINARY|NV|P(15)|'&',
446 OC_BINARY|NV|P(25)|'/', OC_BINARY|NV|P(25)|'%', OC_BINARY|NV|P(15)|'&', OC_BINARY|NV|P(25)|'*', 448 OC_BINARY|NV|P(25)|'/', OC_BINARY|NV|P(25)|'%', OC_BINARY|NV|P(15)|'&', OC_BINARY|NV|P(25)|'*',
447 OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3, OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1, 449 OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3, OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1,
448#define TI_LESS (OC_COMPARE|VV|P(39)|2) 450#define TI_LESS (OC_COMPARE|VV|P(39)|2)
@@ -1301,7 +1303,7 @@ static uint32_t next_token(uint32_t expected)
1301 save_tclass = tc; 1303 save_tclass = tc;
1302 save_info = t_info; 1304 save_info = t_info;
1303 tc = TC_BINOPX; 1305 tc = TC_BINOPX;
1304 t_info = OC_CONCAT | SS | P(35); 1306 t_info = OC_CONCAT | SS | PRECEDENCE(35);
1305 } 1307 }
1306 1308
1307 t_tclass = tc; 1309 t_tclass = tc;
@@ -1361,9 +1363,8 @@ static node *parse_expr(uint32_t term_tc)
1361{ 1363{
1362 node sn; 1364 node sn;
1363 node *cn = &sn; 1365 node *cn = &sn;
1364 node *vn, *glptr; 1366 node *glptr;
1365 uint32_t tc, expected_tc; 1367 uint32_t tc, expected_tc;
1366 var *v;
1367 1368
1368 debug_printf_parse("%s() term_tc(%x):", __func__, term_tc); 1369 debug_printf_parse("%s() term_tc(%x):", __func__, term_tc);
1369 debug_parse_print_tc(term_tc); 1370 debug_parse_print_tc(term_tc);
@@ -1374,11 +1375,12 @@ static node *parse_expr(uint32_t term_tc)
1374 expected_tc = TS_OPERAND | TS_UOPPRE | TC_REGEXP | term_tc; 1375 expected_tc = TS_OPERAND | TS_UOPPRE | TC_REGEXP | term_tc;
1375 1376
1376 while (!((tc = next_token(expected_tc)) & term_tc)) { 1377 while (!((tc = next_token(expected_tc)) & term_tc)) {
1378 node *vn;
1377 1379
1378 if (glptr && (t_info == TI_LESS)) { 1380 if (glptr && (t_info == TI_LESS)) {
1379 /* input redirection (<) attached to glptr node */ 1381 /* input redirection (<) attached to glptr node */
1380 debug_printf_parse("%s: input redir\n", __func__); 1382 debug_printf_parse("%s: input redir\n", __func__);
1381 cn = glptr->l.n = new_node(OC_CONCAT | SS | P(37)); 1383 cn = glptr->l.n = new_node(OC_CONCAT | SS | PRECEDENCE(37));
1382 cn->a.n = glptr; 1384 cn->a.n = glptr;
1383 expected_tc = TS_OPERAND | TS_UOPPRE; 1385 expected_tc = TS_OPERAND | TS_UOPPRE;
1384 glptr = NULL; 1386 glptr = NULL;
@@ -1390,24 +1392,42 @@ static node *parse_expr(uint32_t term_tc)
1390 * previous operators with higher priority */ 1392 * previous operators with higher priority */
1391 vn = cn; 1393 vn = cn;
1392 while (((t_info & PRIMASK) > (vn->a.n->info & PRIMASK2)) 1394 while (((t_info & PRIMASK) > (vn->a.n->info & PRIMASK2))
1393 || ((t_info == vn->info) && t_info == TI_COLON) 1395 || (t_info == vn->info && t_info == TI_COLON)
1394 ) { 1396 ) {
1395 vn = vn->a.n; 1397 vn = vn->a.n;
1396 if (!vn->a.n) syntax_error(EMSG_UNEXP_TOKEN); 1398 if (!vn->a.n) syntax_error(EMSG_UNEXP_TOKEN);
1397 } 1399 }
1398 if (t_info == TI_TERNARY) 1400 if (t_info == TI_TERNARY)
1399//TODO: why? 1401//TODO: why?
1400 t_info += P(6); 1402 t_info += PRECEDENCE(6);
1401 cn = vn->a.n->r.n = new_node(t_info); 1403 cn = vn->a.n->r.n = new_node(t_info);
1402 cn->a.n = vn->a.n; 1404 cn->a.n = vn->a.n;
1403 if (tc & TS_BINOP) { 1405 if (tc & TS_BINOP) {
1404 cn->l.n = vn; 1406 cn->l.n = vn;
1405//FIXME: this is the place to detect and reject assignments to non-lvalues. 1407
1406//Currently we allow "assignments" to consts and temporaries, nonsense like this: 1408 /* Prevent:
1407// awk 'BEGIN { "qwe" = 1 }' 1409 * awk 'BEGIN { "qwe" = 1 }'
1408// awk 'BEGIN { 7 *= 7 }' 1410 * awk 'BEGIN { 7 *= 7 }'
1409// awk 'BEGIN { length("qwe") = 1 }' 1411 * awk 'BEGIN { length("qwe") = 1 }'
1410// awk 'BEGIN { (1+1) += 3 }' 1412 * awk 'BEGIN { (1+1) += 3 }'
1413 */
1414 /* Assignment? (including *= and friends) */
1415 if (((t_info & OPCLSMASK) == OC_MOVE)
1416 || ((t_info & OPCLSMASK) == OC_REPLACE)
1417 ) {
1418 debug_printf_parse("%s: MOVE/REPLACE vn->info:%08x\n", __func__, vn->info);
1419 /* Left side is a (variable or array element)
1420 * or function argument
1421 * or $FIELD ?
1422 */
1423 if ((vn->info & OPCLSMASK) != OC_VAR
1424 && (vn->info & OPCLSMASK) != OC_FNARG
1425 && (vn->info & OPCLSMASK) != OC_FIELD
1426 ) {
1427 syntax_error(EMSG_UNEXP_TOKEN); /* no. bad */
1428 }
1429 }
1430
1411 expected_tc = TS_OPERAND | TS_UOPPRE | TC_REGEXP; 1431 expected_tc = TS_OPERAND | TS_UOPPRE | TC_REGEXP;
1412 if (t_info == TI_PGETLINE) { 1432 if (t_info == TI_PGETLINE) {
1413 /* it's a pipe */ 1433 /* it's a pipe */
@@ -1443,6 +1463,8 @@ static node *parse_expr(uint32_t term_tc)
1443 /* one should be very careful with switch on tclass - 1463 /* one should be very careful with switch on tclass -
1444 * only simple tclasses should be used (TC_xyz, not TS_xyz) */ 1464 * only simple tclasses should be used (TC_xyz, not TS_xyz) */
1445 switch (tc) { 1465 switch (tc) {
1466 var *v;
1467
1446 case TC_VARIABLE: 1468 case TC_VARIABLE:
1447 case TC_ARRAY: 1469 case TC_ARRAY:
1448 debug_printf_parse("%s: TC_VARIABLE | TC_ARRAY\n", __func__); 1470 debug_printf_parse("%s: TC_VARIABLE | TC_ARRAY\n", __func__);
@@ -1463,14 +1485,14 @@ static node *parse_expr(uint32_t term_tc)
1463 case TC_NUMBER: 1485 case TC_NUMBER:
1464 case TC_STRING: 1486 case TC_STRING:
1465 debug_printf_parse("%s: TC_NUMBER | TC_STRING\n", __func__); 1487 debug_printf_parse("%s: TC_NUMBER | TC_STRING\n", __func__);
1466 cn->info = OC_VAR; 1488 cn->info = OC_CONST;
1467 v = cn->l.v = xzalloc(sizeof(var)); 1489 v = cn->l.v = xzalloc(sizeof(var));
1468 if (tc & TC_NUMBER) 1490 if (tc & TC_NUMBER) {
1469 setvar_i(v, t_double); 1491 setvar_i(v, t_double);
1470 else { 1492 } else {
1471 setvar_s(v, t_string); 1493 setvar_s(v, t_string);
1472 expected_tc &= ~TC_UOPPOST; /* "str"++ is not allowed */
1473 } 1494 }
1495 expected_tc &= ~TC_UOPPOST; /* NUM++, "str"++ not allowed */
1474 break; 1496 break;
1475 1497
1476 case TC_REGEXP: 1498 case TC_REGEXP:
@@ -3124,6 +3146,8 @@ static var *evaluate(node *op, var *res)
3124 3146
3125 /* -- recursive node type -- */ 3147 /* -- recursive node type -- */
3126 3148
3149 case XC( OC_CONST ):
3150 debug_printf_eval("CONST ");
3127 case XC( OC_VAR ): 3151 case XC( OC_VAR ):
3128 debug_printf_eval("VAR\n"); 3152 debug_printf_eval("VAR\n");
3129 L.v = op->l.v; 3153 L.v = op->l.v;
diff --git a/testsuite/awk.tests b/testsuite/awk.tests
index 8ab1c6891..cdab93d21 100755
--- a/testsuite/awk.tests
+++ b/testsuite/awk.tests
@@ -547,4 +547,9 @@ testing 'awk does not split on CR (char 13)' \
547 'word1 word2 word3\r word2 word3\r\n' \ 547 'word1 word2 word3\r word2 word3\r\n' \
548 '' 'word1 word2 word3\r' 548 '' 'word1 word2 word3\r'
549 549
550testing "awk = has higher precedence than == (despite what gawk manpage claims)" \
551 "awk 'BEGIN { v=1; print 2==v; print 2==v=2; print v; print v=3==3; print v}'" \
552 '0\n1\n2\n1\n3\n' \
553 '' ''
554
550exit $FAILCOUNT 555exit $FAILCOUNT