diff options
author | Ron Yorston <rmy@pobox.com> | 2021-07-17 10:35:24 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2021-07-17 10:35:24 +0100 |
commit | 36e9606c1d1edfbc9e4eba31b4c498dba361ac98 (patch) | |
tree | 492a436d5fba5a545e7155a115679e0669e9bed6 | |
parent | dcca23dc4cf9b51a1c2576360fea190e9b71204e (diff) | |
parent | dabbeeb79356eef78528acd55e1f143ae80372f7 (diff) | |
download | busybox-w32-36e9606c1d1edfbc9e4eba31b4c498dba361ac98.tar.gz busybox-w32-36e9606c1d1edfbc9e4eba31b4c498dba361ac98.tar.bz2 busybox-w32-36e9606c1d1edfbc9e4eba31b4c498dba361ac98.zip |
Merge branch 'busybox' into merge
-rw-r--r-- | editors/awk.c | 316 | ||||
-rw-r--r-- | editors/vi.c | 159 | ||||
-rw-r--r-- | networking/tc.c | 42 | ||||
-rwxr-xr-x | testsuite/awk.tests | 27 |
4 files changed, 358 insertions, 186 deletions
diff --git a/editors/awk.c b/editors/awk.c index 5a26d389f..75f757dbb 100644 --- a/editors/awk.c +++ b/editors/awk.c | |||
@@ -199,77 +199,78 @@ typedef struct tsplitter_s { | |||
199 | 199 | ||
200 | /* simple token classes */ | 200 | /* simple token classes */ |
201 | /* order and hex values are very important!!! See next_token() */ | 201 | /* order and hex values are very important!!! See next_token() */ |
202 | #define TC_LPAREN (1 << 0) /* ( */ | 202 | #define TC_LPAREN (1 << 0) /* ( */ |
203 | #define TC_RPAREN (1 << 1) /* ) */ | 203 | #define TC_RPAREN (1 << 1) /* ) */ |
204 | #define TC_REGEXP (1 << 2) /* /.../ */ | 204 | #define TC_REGEXP (1 << 2) /* /.../ */ |
205 | #define TC_OUTRDR (1 << 3) /* | > >> */ | 205 | #define TC_OUTRDR (1 << 3) /* | > >> */ |
206 | #define TC_UOPPOST (1 << 4) /* unary postfix operator ++ -- */ | 206 | #define TC_UOPPOST (1 << 4) /* unary postfix operator ++ -- */ |
207 | #define TC_UOPPRE1 (1 << 5) /* unary prefix operator ++ -- $ */ | 207 | #define TC_UOPPRE1 (1 << 5) /* unary prefix operator ++ -- $ */ |
208 | #define TC_BINOPX (1 << 6) /* two-opnd operator */ | 208 | #define TC_BINOPX (1 << 6) /* two-opnd operator */ |
209 | #define TC_IN (1 << 7) /* 'in' */ | 209 | #define TC_IN (1 << 7) /* 'in' */ |
210 | #define TC_COMMA (1 << 8) /* , */ | 210 | #define TC_COMMA (1 << 8) /* , */ |
211 | #define TC_PIPE (1 << 9) /* input redirection pipe | */ | 211 | #define TC_PIPE (1 << 9) /* input redirection pipe | */ |
212 | #define TC_UOPPRE2 (1 << 10) /* unary prefix operator + - ! */ | 212 | #define TC_UOPPRE2 (1 << 10) /* unary prefix operator + - ! */ |
213 | #define TC_ARRTERM (1 << 11) /* ] */ | 213 | #define TC_ARRTERM (1 << 11) /* ] */ |
214 | #define TC_LBRACE (1 << 12) /* { */ | 214 | #define TC_LBRACE (1 << 12) /* { */ |
215 | #define TC_RBRACE (1 << 13) /* } */ | 215 | #define TC_RBRACE (1 << 13) /* } */ |
216 | #define TC_SEMICOL (1 << 14) /* ; */ | 216 | #define TC_SEMICOL (1 << 14) /* ; */ |
217 | #define TC_NEWLINE (1 << 15) | 217 | #define TC_NEWLINE (1 << 15) |
218 | #define TC_STATX (1 << 16) /* ctl statement (for, next...) */ | 218 | #define TC_STATX (1 << 16) /* ctl statement (for, next...) */ |
219 | #define TC_WHILE (1 << 17) /* 'while' */ | 219 | #define TC_WHILE (1 << 17) /* 'while' */ |
220 | #define TC_ELSE (1 << 18) /* 'else' */ | 220 | #define TC_ELSE (1 << 18) /* 'else' */ |
221 | #define TC_BUILTIN (1 << 19) | 221 | #define TC_BUILTIN (1 << 19) |
222 | /* This costs ~50 bytes of code. | 222 | /* This costs ~50 bytes of code. |
223 | * A separate class to support deprecated "length" form. If we don't need that | 223 | * A separate class to support deprecated "length" form. If we don't need that |
224 | * (i.e. if we demand that only "length()" with () is valid), then TC_LENGTH | 224 | * (i.e. if we demand that only "length()" with () is valid), then TC_LENGTH |
225 | * can be merged with TC_BUILTIN: | 225 | * can be merged with TC_BUILTIN: |
226 | */ | 226 | */ |
227 | #define TC_LENGTH (1 << 20) /* 'length' */ | 227 | #define TC_LENGTH (1 << 20) /* 'length' */ |
228 | #define TC_GETLINE (1 << 21) /* 'getline' */ | 228 | #define TC_GETLINE (1 << 21) /* 'getline' */ |
229 | #define TC_FUNCDECL (1 << 22) /* 'function' 'func' */ | 229 | #define TC_FUNCDECL (1 << 22) /* 'function' 'func' */ |
230 | #define TC_BEGIN (1 << 23) /* 'BEGIN' */ | 230 | #define TC_BEGIN (1 << 23) /* 'BEGIN' */ |
231 | #define TC_END (1 << 24) /* 'END' */ | 231 | #define TC_END (1 << 24) /* 'END' */ |
232 | #define TC_EOF (1 << 25) | 232 | #define TC_EOF (1 << 25) |
233 | #define TC_VARIABLE (1 << 26) /* name */ | 233 | #define TC_VARIABLE (1 << 26) /* name */ |
234 | #define TC_ARRAY (1 << 27) /* name[ */ | 234 | #define TC_ARRAY (1 << 27) /* name[ */ |
235 | #define TC_FUNCTION (1 << 28) /* name( */ | 235 | #define TC_FUNCTION (1 << 28) /* name( */ |
236 | #define TC_STRING (1 << 29) /* "..." */ | 236 | #define TC_STRING (1 << 29) /* "..." */ |
237 | #define TC_NUMBER (1 << 30) | 237 | #define TC_NUMBER (1 << 30) |
238 | 238 | ||
239 | #ifndef debug_parse_print_tc | 239 | #ifndef debug_parse_print_tc |
240 | #define debug_parse_print_tc(n) do { \ | 240 | static void debug_parse_print_tc(uint32_t n) |
241 | if ((n) & TC_LPAREN ) debug_printf_parse(" LPAREN" ); \ | 241 | { |
242 | if ((n) & TC_RPAREN ) debug_printf_parse(" RPAREN" ); \ | 242 | if (n & TC_LPAREN ) debug_printf_parse(" LPAREN" ); |
243 | if ((n) & TC_REGEXP ) debug_printf_parse(" REGEXP" ); \ | 243 | if (n & TC_RPAREN ) debug_printf_parse(" RPAREN" ); |
244 | if ((n) & TC_OUTRDR ) debug_printf_parse(" OUTRDR" ); \ | 244 | if (n & TC_REGEXP ) debug_printf_parse(" REGEXP" ); |
245 | if ((n) & TC_UOPPOST ) debug_printf_parse(" UOPPOST" ); \ | 245 | if (n & TC_OUTRDR ) debug_printf_parse(" OUTRDR" ); |
246 | if ((n) & TC_UOPPRE1 ) debug_printf_parse(" UOPPRE1" ); \ | 246 | if (n & TC_UOPPOST ) debug_printf_parse(" UOPPOST" ); |
247 | if ((n) & TC_BINOPX ) debug_printf_parse(" BINOPX" ); \ | 247 | if (n & TC_UOPPRE1 ) debug_printf_parse(" UOPPRE1" ); |
248 | if ((n) & TC_IN ) debug_printf_parse(" IN" ); \ | 248 | if (n & TC_BINOPX ) debug_printf_parse(" BINOPX" ); |
249 | if ((n) & TC_COMMA ) debug_printf_parse(" COMMA" ); \ | 249 | if (n & TC_IN ) debug_printf_parse(" IN" ); |
250 | if ((n) & TC_PIPE ) debug_printf_parse(" PIPE" ); \ | 250 | if (n & TC_COMMA ) debug_printf_parse(" COMMA" ); |
251 | if ((n) & TC_UOPPRE2 ) debug_printf_parse(" UOPPRE2" ); \ | 251 | if (n & TC_PIPE ) debug_printf_parse(" PIPE" ); |
252 | if ((n) & TC_ARRTERM ) debug_printf_parse(" ARRTERM" ); \ | 252 | if (n & TC_UOPPRE2 ) debug_printf_parse(" UOPPRE2" ); |
253 | if ((n) & TC_LBRACE ) debug_printf_parse(" LBRACE" ); \ | 253 | if (n & TC_ARRTERM ) debug_printf_parse(" ARRTERM" ); |
254 | if ((n) & TC_RBRACE ) debug_printf_parse(" RBRACE" ); \ | 254 | if (n & TC_LBRACE ) debug_printf_parse(" LBRACE" ); |
255 | if ((n) & TC_SEMICOL ) debug_printf_parse(" SEMICOL" ); \ | 255 | if (n & TC_RBRACE ) debug_printf_parse(" RBRACE" ); |
256 | if ((n) & TC_NEWLINE ) debug_printf_parse(" NEWLINE" ); \ | 256 | if (n & TC_SEMICOL ) debug_printf_parse(" SEMICOL" ); |
257 | if ((n) & TC_STATX ) debug_printf_parse(" STATX" ); \ | 257 | if (n & TC_NEWLINE ) debug_printf_parse(" NEWLINE" ); |
258 | if ((n) & TC_WHILE ) debug_printf_parse(" WHILE" ); \ | 258 | if (n & TC_STATX ) debug_printf_parse(" STATX" ); |
259 | if ((n) & TC_ELSE ) debug_printf_parse(" ELSE" ); \ | 259 | if (n & TC_WHILE ) debug_printf_parse(" WHILE" ); |
260 | if ((n) & TC_BUILTIN ) debug_printf_parse(" BUILTIN" ); \ | 260 | if (n & TC_ELSE ) debug_printf_parse(" ELSE" ); |
261 | if ((n) & TC_LENGTH ) debug_printf_parse(" LENGTH" ); \ | 261 | if (n & TC_BUILTIN ) debug_printf_parse(" BUILTIN" ); |
262 | if ((n) & TC_GETLINE ) debug_printf_parse(" GETLINE" ); \ | 262 | if (n & TC_LENGTH ) debug_printf_parse(" LENGTH" ); |
263 | if ((n) & TC_FUNCDECL) debug_printf_parse(" FUNCDECL"); \ | 263 | if (n & TC_GETLINE ) debug_printf_parse(" GETLINE" ); |
264 | if ((n) & TC_BEGIN ) debug_printf_parse(" BEGIN" ); \ | 264 | if (n & TC_FUNCDECL) debug_printf_parse(" FUNCDECL"); |
265 | if ((n) & TC_END ) debug_printf_parse(" END" ); \ | 265 | if (n & TC_BEGIN ) debug_printf_parse(" BEGIN" ); |
266 | if ((n) & TC_EOF ) debug_printf_parse(" EOF" ); \ | 266 | if (n & TC_END ) debug_printf_parse(" END" ); |
267 | if ((n) & TC_VARIABLE) debug_printf_parse(" VARIABLE"); \ | 267 | if (n & TC_EOF ) debug_printf_parse(" EOF" ); |
268 | if ((n) & TC_ARRAY ) debug_printf_parse(" ARRAY" ); \ | 268 | if (n & TC_VARIABLE) debug_printf_parse(" VARIABLE"); |
269 | if ((n) & TC_FUNCTION) debug_printf_parse(" FUNCTION"); \ | 269 | if (n & TC_ARRAY ) debug_printf_parse(" ARRAY" ); |
270 | if ((n) & TC_STRING ) debug_printf_parse(" STRING" ); \ | 270 | if (n & TC_FUNCTION) debug_printf_parse(" FUNCTION"); |
271 | if ((n) & TC_NUMBER ) debug_printf_parse(" NUMBER" ); \ | 271 | if (n & TC_STRING ) debug_printf_parse(" STRING" ); |
272 | } while (0) | 272 | if (n & TC_NUMBER ) debug_printf_parse(" NUMBER" ); |
273 | } | ||
273 | #endif | 274 | #endif |
274 | 275 | ||
275 | /* combined token classes ("token [class] sets") */ | 276 | /* combined token classes ("token [class] sets") */ |
@@ -417,7 +418,7 @@ static const char tokenlist[] ALIGN1 = | |||
417 | "\5close" "\6system" "\6fflush" "\5atan2" | 418 | "\5close" "\6system" "\6fflush" "\5atan2" |
418 | "\3cos" "\3exp" "\3int" "\3log" | 419 | "\3cos" "\3exp" "\3int" "\3log" |
419 | "\4rand" "\3sin" "\4sqrt" "\5srand" | 420 | "\4rand" "\3sin" "\4sqrt" "\5srand" |
420 | "\6gensub" "\4gsub" "\5index" /* "\6length" was here */ | 421 | "\6gensub" "\4gsub" "\5index" /* "\6length" was here */ |
421 | "\5match" "\5split" "\7sprintf" "\3sub" | 422 | "\5match" "\5split" "\7sprintf" "\3sub" |
422 | "\6substr" "\7systime" "\10strftime" "\6mktime" | 423 | "\6substr" "\7systime" "\10strftime" "\6mktime" |
423 | "\7tolower" "\7toupper" NTC | 424 | "\7tolower" "\7toupper" NTC |
@@ -462,8 +463,7 @@ static const uint32_t tokeninfo[] ALIGN4 = { | |||
462 | 0, | 463 | 0, |
463 | 0, /* \n */ | 464 | 0, /* \n */ |
464 | ST_IF, ST_DO, ST_FOR, OC_BREAK, | 465 | ST_IF, ST_DO, ST_FOR, OC_BREAK, |
465 | #define TI_PRINT OC_PRINT | 466 | OC_CONTINUE, OC_DELETE|Rx, OC_PRINT, |
466 | OC_CONTINUE, OC_DELETE|Rx, TI_PRINT, | ||
467 | OC_PRINTF, OC_NEXT, OC_NEXTFILE, | 467 | OC_PRINTF, OC_NEXT, OC_NEXTFILE, |
468 | OC_RETURN|Vx, OC_EXIT|Nx, | 468 | OC_RETURN|Vx, OC_EXIT|Nx, |
469 | ST_WHILE, | 469 | ST_WHILE, |
@@ -908,25 +908,23 @@ static double my_strtod(char **pp) | |||
908 | 908 | ||
909 | /* -------- working with variables (set/get/copy/etc) -------- */ | 909 | /* -------- working with variables (set/get/copy/etc) -------- */ |
910 | 910 | ||
911 | static int fmt_num(char *b, int size, const char *format, double n, int int_as_int) | 911 | static void fmt_num(const char *format, double n) |
912 | { | 912 | { |
913 | int r = 0; | 913 | if (n == (long long)n) { |
914 | char c; | 914 | snprintf(g_buf, MAXVARFMT, "%"LL_FMT"d", (long long)n); |
915 | const char *s = format; | ||
916 | |||
917 | if (int_as_int && n == (long long)n) { | ||
918 | r = snprintf(b, size, "%"LL_FMT"d", (long long)n); | ||
919 | } else { | 915 | } else { |
916 | const char *s = format; | ||
917 | char c; | ||
918 | |||
920 | do { c = *s; } while (c && *++s); | 919 | do { c = *s; } while (c && *++s); |
921 | if (strchr("diouxX", c)) { | 920 | if (strchr("diouxX", c)) { |
922 | r = snprintf(b, size, format, (int)n); | 921 | snprintf(g_buf, MAXVARFMT, format, (int)n); |
923 | } else if (strchr("eEfFgGaA", c)) { | 922 | } else if (strchr("eEfFgGaA", c)) { |
924 | r = snprintf(b, size, format, n); | 923 | snprintf(g_buf, MAXVARFMT, format, n); |
925 | } else { | 924 | } else { |
926 | syntax_error(EMSG_INV_FMT); | 925 | syntax_error(EMSG_INV_FMT); |
927 | } | 926 | } |
928 | } | 927 | } |
929 | return r; | ||
930 | } | 928 | } |
931 | 929 | ||
932 | static xhash *iamarray(var *a) | 930 | static xhash *iamarray(var *a) |
@@ -1003,7 +1001,7 @@ static const char *getvar_s(var *v) | |||
1003 | { | 1001 | { |
1004 | /* if v is numeric and has no cached string, convert it to string */ | 1002 | /* if v is numeric and has no cached string, convert it to string */ |
1005 | if ((v->type & (VF_NUMBER | VF_CACHED)) == VF_NUMBER) { | 1003 | if ((v->type & (VF_NUMBER | VF_CACHED)) == VF_NUMBER) { |
1006 | fmt_num(g_buf, MAXVARFMT, getvar_s(intvar[CONVFMT]), v->number, TRUE); | 1004 | fmt_num(getvar_s(intvar[CONVFMT]), v->number); |
1007 | v->string = xstrdup(g_buf); | 1005 | v->string = xstrdup(g_buf); |
1008 | v->type |= VF_CACHED; | 1006 | v->type |= VF_CACHED; |
1009 | } | 1007 | } |
@@ -1596,8 +1594,8 @@ static void chain_group(void) | |||
1596 | chain_until_rbrace(); | 1594 | chain_until_rbrace(); |
1597 | return; | 1595 | return; |
1598 | } | 1596 | } |
1599 | if (tc & (TS_OPSEQ | TC_SEMICOL | TC_NEWLINE)) { | 1597 | if (tc & (TS_OPSEQ | TC_SEMICOL)) { |
1600 | debug_printf_parse("%s: TS_OPSEQ | TC_SEMICOL | TC_NEWLINE\n", __func__); | 1598 | debug_printf_parse("%s: TS_OPSEQ | TC_SEMICOL\n", __func__); |
1601 | rollback_token(); | 1599 | rollback_token(); |
1602 | chain_expr(OC_EXEC | Vx); | 1600 | chain_expr(OC_EXEC | Vx); |
1603 | return; | 1601 | return; |
@@ -1641,7 +1639,7 @@ static void chain_group(void) | |||
1641 | debug_printf_parse("%s: ST_FOR\n", __func__); | 1639 | debug_printf_parse("%s: ST_FOR\n", __func__); |
1642 | next_token(TC_LPAREN); | 1640 | next_token(TC_LPAREN); |
1643 | n2 = parse_expr(TC_SEMICOL | TC_RPAREN); | 1641 | n2 = parse_expr(TC_SEMICOL | TC_RPAREN); |
1644 | if (t_tclass & TC_RPAREN) { /* for-in */ | 1642 | if (t_tclass & TC_RPAREN) { /* for (I in ARRAY) */ |
1645 | if (!n2 || n2->info != TI_IN) | 1643 | if (!n2 || n2->info != TI_IN) |
1646 | syntax_error(EMSG_UNEXP_TOKEN); | 1644 | syntax_error(EMSG_UNEXP_TOKEN); |
1647 | n = chain_node(OC_WALKINIT | VV); | 1645 | n = chain_node(OC_WALKINIT | VV); |
@@ -1678,16 +1676,18 @@ static void chain_group(void) | |||
1678 | case OC_BREAK: | 1676 | case OC_BREAK: |
1679 | debug_printf_parse("%s: OC_BREAK\n", __func__); | 1677 | debug_printf_parse("%s: OC_BREAK\n", __func__); |
1680 | n = chain_node(OC_EXEC); | 1678 | n = chain_node(OC_EXEC); |
1679 | if (!break_ptr) | ||
1680 | syntax_error("'break' not in a loop"); | ||
1681 | n->a.n = break_ptr; | 1681 | n->a.n = break_ptr; |
1682 | //TODO: if break_ptr is NULL, syntax error (not in the loop)? | ||
1683 | chain_expr(t_info); | 1682 | chain_expr(t_info); |
1684 | break; | 1683 | break; |
1685 | 1684 | ||
1686 | case OC_CONTINUE: | 1685 | case OC_CONTINUE: |
1687 | debug_printf_parse("%s: OC_CONTINUE\n", __func__); | 1686 | debug_printf_parse("%s: OC_CONTINUE\n", __func__); |
1688 | n = chain_node(OC_EXEC); | 1687 | n = chain_node(OC_EXEC); |
1688 | if (!continue_ptr) | ||
1689 | syntax_error("'continue' not in a loop"); | ||
1689 | n->a.n = continue_ptr; | 1690 | n->a.n = continue_ptr; |
1690 | //TODO: if continue_ptr is NULL, syntax error (not in the loop)? | ||
1691 | chain_expr(t_info); | 1691 | chain_expr(t_info); |
1692 | break; | 1692 | break; |
1693 | 1693 | ||
@@ -1707,20 +1707,15 @@ static void parse_program(char *p) | |||
1707 | for (;;) { | 1707 | for (;;) { |
1708 | uint32_t tclass; | 1708 | uint32_t tclass; |
1709 | 1709 | ||
1710 | tclass = next_token(TC_EOF | TS_OPSEQ | TC_LBRACE | | 1710 | tclass = next_token(TS_OPSEQ | TC_LBRACE | TC_BEGIN | TC_END | TC_FUNCDECL |
1711 | TC_SEMICOL | TC_NEWLINE | TC_BEGIN | TC_END | TC_FUNCDECL); | 1711 | | TC_EOF | TC_NEWLINE /* but not TC_SEMICOL */); |
1712 | 1712 | got_tok: | |
1713 | if (tclass == TC_EOF) { | 1713 | if (tclass == TC_EOF) { |
1714 | debug_printf_parse("%s: TC_EOF\n", __func__); | 1714 | debug_printf_parse("%s: TC_EOF\n", __func__); |
1715 | break; | 1715 | break; |
1716 | } | 1716 | } |
1717 | if (tclass & (TC_SEMICOL | TC_NEWLINE)) { | 1717 | if (tclass == TC_NEWLINE) { |
1718 | debug_printf_parse("%s: TC_SEMICOL | TC_NEWLINE\n", __func__); | 1718 | debug_printf_parse("%s: TC_NEWLINE\n", __func__); |
1719 | //NB: gawk allows many newlines, but does not allow more than one semicolon: | ||
1720 | // BEGIN {...}<newline>;<newline>; | ||
1721 | //would complain "each rule must have a pattern or an action part". | ||
1722 | //Same message for | ||
1723 | // ; BEGIN {...} | ||
1724 | continue; | 1719 | continue; |
1725 | } | 1720 | } |
1726 | if (tclass == TC_BEGIN) { | 1721 | if (tclass == TC_BEGIN) { |
@@ -1729,7 +1724,7 @@ static void parse_program(char *p) | |||
1729 | /* ensure there is no newline between BEGIN and { */ | 1724 | /* ensure there is no newline between BEGIN and { */ |
1730 | next_token(TC_LBRACE); | 1725 | next_token(TC_LBRACE); |
1731 | chain_until_rbrace(); | 1726 | chain_until_rbrace(); |
1732 | continue; | 1727 | goto next_tok; |
1733 | } | 1728 | } |
1734 | if (tclass == TC_END) { | 1729 | if (tclass == TC_END) { |
1735 | debug_printf_parse("%s: TC_END\n", __func__); | 1730 | debug_printf_parse("%s: TC_END\n", __func__); |
@@ -1737,7 +1732,7 @@ static void parse_program(char *p) | |||
1737 | /* ensure there is no newline between END and { */ | 1732 | /* ensure there is no newline between END and { */ |
1738 | next_token(TC_LBRACE); | 1733 | next_token(TC_LBRACE); |
1739 | chain_until_rbrace(); | 1734 | chain_until_rbrace(); |
1740 | continue; | 1735 | goto next_tok; |
1741 | } | 1736 | } |
1742 | if (tclass == TC_FUNCDECL) { | 1737 | if (tclass == TC_FUNCDECL) { |
1743 | func *f; | 1738 | func *f; |
@@ -1772,7 +1767,7 @@ static void parse_program(char *p) | |||
1772 | continue; | 1767 | continue; |
1773 | chain_until_rbrace(); | 1768 | chain_until_rbrace(); |
1774 | hash_clear(ahash); | 1769 | hash_clear(ahash); |
1775 | continue; | 1770 | goto next_tok; |
1776 | } | 1771 | } |
1777 | seq = &mainseq; | 1772 | seq = &mainseq; |
1778 | if (tclass & TS_OPSEQ) { | 1773 | if (tclass & TS_OPSEQ) { |
@@ -1784,23 +1779,34 @@ static void parse_program(char *p) | |||
1784 | cn->l.n = parse_expr(TC_SEMICOL | TC_NEWLINE | TC_EOF | TC_LBRACE); | 1779 | cn->l.n = parse_expr(TC_SEMICOL | TC_NEWLINE | TC_EOF | TC_LBRACE); |
1785 | if (t_tclass == TC_LBRACE) { | 1780 | if (t_tclass == TC_LBRACE) { |
1786 | debug_printf_parse("%s: TC_LBRACE\n", __func__); | 1781 | debug_printf_parse("%s: TC_LBRACE\n", __func__); |
1787 | rollback_token(); | 1782 | chain_until_rbrace(); |
1788 | chain_group(); | ||
1789 | } else { | 1783 | } else { |
1790 | /* no action, assume default "{ print }" */ | 1784 | /* no action, assume default "{ print }" */ |
1791 | debug_printf_parse("%s: !TC_LBRACE\n", __func__); | 1785 | debug_printf_parse("%s: !TC_LBRACE\n", __func__); |
1792 | chain_node(OC_PRINT); | 1786 | chain_node(OC_PRINT); |
1793 | } | 1787 | } |
1794 | cn->r.n = mainseq.last; | 1788 | cn->r.n = mainseq.last; |
1795 | continue; | 1789 | goto next_tok; |
1796 | } | 1790 | } |
1797 | /* tclass == TC_LBRACE */ | 1791 | /* tclass == TC_LBRACE */ |
1798 | debug_printf_parse("%s: TC_LBRACE(?)\n", __func__); | 1792 | debug_printf_parse("%s: TC_LBRACE(?)\n", __func__); |
1799 | chain_until_rbrace(); | 1793 | chain_until_rbrace(); |
1800 | } | 1794 | next_tok: |
1795 | /* Same as next_token() at the top of the loop, + TC_SEMICOL */ | ||
1796 | tclass = next_token(TS_OPSEQ | TC_LBRACE | TC_BEGIN | TC_END | TC_FUNCDECL | ||
1797 | | TC_EOF | TC_NEWLINE | TC_SEMICOL); | ||
1798 | /* gawk allows many newlines, but does not allow more than one semicolon: | ||
1799 | * BEGIN {...}<newline>;<newline>; | ||
1800 | * would complain "each rule must have a pattern or an action part". | ||
1801 | * Same message for | ||
1802 | * ; BEGIN {...} | ||
1803 | */ | ||
1804 | if (tclass != TC_SEMICOL) | ||
1805 | goto got_tok; /* use this token */ | ||
1806 | /* else: loop back - ate the semicolon, get and use _next_ token */ | ||
1807 | } /* for (;;) */ | ||
1801 | } | 1808 | } |
1802 | 1809 | ||
1803 | |||
1804 | /* -------- program execution part -------- */ | 1810 | /* -------- program execution part -------- */ |
1805 | 1811 | ||
1806 | /* temporary variables allocator */ | 1812 | /* temporary variables allocator */ |
@@ -2332,14 +2338,11 @@ static int awk_getline(rstream *rsm, var *v) | |||
2332 | #if !ENABLE_FEATURE_AWK_GNU_EXTENSIONS | 2338 | #if !ENABLE_FEATURE_AWK_GNU_EXTENSIONS |
2333 | # define awk_printf(a, b) awk_printf(a) | 2339 | # define awk_printf(a, b) awk_printf(a) |
2334 | #endif | 2340 | #endif |
2335 | static char *awk_printf(node *n, int *len) | 2341 | static char *awk_printf(node *n, size_t *len) |
2336 | { | 2342 | { |
2337 | char *b = NULL; | 2343 | char *b; |
2338 | char *fmt, *s, *f; | 2344 | char *fmt, *f; |
2339 | const char *s1; | 2345 | size_t i; |
2340 | int i, j, incr, bsize; | ||
2341 | char c, c1; | ||
2342 | var *arg; | ||
2343 | 2346 | ||
2344 | //tmpvar = nvalloc(1); | 2347 | //tmpvar = nvalloc(1); |
2345 | #define TMPVAR (&G.awk_printf__tmpvar) | 2348 | #define TMPVAR (&G.awk_printf__tmpvar) |
@@ -2352,8 +2355,15 @@ static char *awk_printf(node *n, int *len) | |||
2352 | // to evaluate() potentially recursing into another awk_printf() can't | 2355 | // to evaluate() potentially recursing into another awk_printf() can't |
2353 | // mangle the value. | 2356 | // mangle the value. |
2354 | 2357 | ||
2358 | b = NULL; | ||
2355 | i = 0; | 2359 | i = 0; |
2356 | while (*f) { | 2360 | while (*f) { /* "print one format spec" loop */ |
2361 | char *s; | ||
2362 | char c; | ||
2363 | char sv; | ||
2364 | var *arg; | ||
2365 | size_t slen; | ||
2366 | |||
2357 | s = f; | 2367 | s = f; |
2358 | while (*f && (*f != '%' || *++f == '%')) | 2368 | while (*f && (*f != '%' || *++f == '%')) |
2359 | f++; | 2369 | f++; |
@@ -2362,40 +2372,64 @@ static char *awk_printf(node *n, int *len) | |||
2362 | syntax_error("%*x formats are not supported"); | 2372 | syntax_error("%*x formats are not supported"); |
2363 | f++; | 2373 | f++; |
2364 | } | 2374 | } |
2365 | |||
2366 | incr = (f - s) + MAXVARFMT; | ||
2367 | b = qrealloc(b, incr + i, &bsize); | ||
2368 | c = *f; | 2375 | c = *f; |
2369 | if (c != '\0') | 2376 | if (!c) { |
2370 | f++; | 2377 | /* Tail of fmt with no percent chars, |
2371 | c1 = *f; | 2378 | * or "....%" (percent seen, but no format specifier char found) |
2379 | */ | ||
2380 | slen = strlen(s); | ||
2381 | goto tail; | ||
2382 | } | ||
2383 | sv = *++f; | ||
2372 | *f = '\0'; | 2384 | *f = '\0'; |
2373 | arg = evaluate(nextarg(&n), TMPVAR); | 2385 | arg = evaluate(nextarg(&n), TMPVAR); |
2374 | 2386 | ||
2375 | j = i; | 2387 | /* Result can be arbitrarily long. Example: |
2376 | if (c == 'c' || !c) { | 2388 | * printf "%99999s", "BOOM" |
2377 | i += sprintf(b+i, s, is_numeric(arg) ? | 2389 | */ |
2378 | (char)getvar_i(arg) : *getvar_s(arg)); | 2390 | if (c == 'c') { |
2379 | } else if (c == 's') { | 2391 | char cc = is_numeric(arg) ? getvar_i(arg) : *getvar_s(arg); |
2380 | s1 = getvar_s(arg); | 2392 | char *r = xasprintf(s, cc ? cc : '^' /* else strlen will be wrong */); |
2381 | b = qrealloc(b, incr+i+strlen(s1), &bsize); | 2393 | slen = strlen(r); |
2382 | i += sprintf(b+i, s, s1); | 2394 | if (cc == '\0') /* if cc is NUL, re-format the string with it */ |
2395 | sprintf(r, s, cc); | ||
2396 | s = r; | ||
2383 | } else { | 2397 | } else { |
2384 | i += fmt_num(b+i, incr, s, getvar_i(arg), FALSE); | 2398 | if (c == 's') { |
2399 | s = xasprintf(s, getvar_s(arg)); | ||
2400 | } else { | ||
2401 | double d = getvar_i(arg); | ||
2402 | if (strchr("diouxX", c)) { | ||
2403 | //TODO: make it wider here (%x -> %llx etc)? | ||
2404 | s = xasprintf(s, (int)d); | ||
2405 | } else if (strchr("eEfFgGaA", c)) { | ||
2406 | s = xasprintf(s, d); | ||
2407 | } else { | ||
2408 | syntax_error(EMSG_INV_FMT); | ||
2409 | } | ||
2410 | } | ||
2411 | slen = strlen(s); | ||
2385 | } | 2412 | } |
2386 | *f = c1; | 2413 | *f = sv; |
2387 | 2414 | ||
2388 | /* if there was an error while sprintf, return value is negative */ | 2415 | if (i == 0) { |
2389 | if (i < j) | 2416 | b = s; |
2390 | i = j; | 2417 | i = slen; |
2418 | continue; | ||
2419 | } | ||
2420 | tail: | ||
2421 | b = xrealloc(b, i + slen + 1); | ||
2422 | strcpy(b + i, s); | ||
2423 | i += slen; | ||
2424 | if (!c) /* tail? */ | ||
2425 | break; | ||
2426 | free(s); | ||
2391 | } | 2427 | } |
2392 | 2428 | ||
2393 | free(fmt); | 2429 | free(fmt); |
2394 | //nvfree(tmpvar, 1); | 2430 | //nvfree(tmpvar, 1); |
2395 | #undef TMPVAR | 2431 | #undef TMPVAR |
2396 | 2432 | ||
2397 | b = xrealloc(b, i + 1); | ||
2398 | b[i] = '\0'; | ||
2399 | #if ENABLE_FEATURE_AWK_GNU_EXTENSIONS | 2433 | #if ENABLE_FEATURE_AWK_GNU_EXTENSIONS |
2400 | if (len) | 2434 | if (len) |
2401 | *len = i; | 2435 | *len = i; |
@@ -2577,10 +2611,11 @@ static NOINLINE var *exec_builtin(node *op, var *res) | |||
2577 | av[2] = av[3] = NULL; | 2611 | av[2] = av[3] = NULL; |
2578 | for (i = 0; i < 4 && op; i++) { | 2612 | for (i = 0; i < 4 && op; i++) { |
2579 | an[i] = nextarg(&op); | 2613 | an[i] = nextarg(&op); |
2580 | if (isr & 0x09000000) | 2614 | if (isr & 0x09000000) { |
2581 | av[i] = evaluate(an[i], TMPVAR(i)); | 2615 | av[i] = evaluate(an[i], TMPVAR(i)); |
2582 | if (isr & 0x08000000) | 2616 | if (isr & 0x08000000) |
2583 | as[i] = getvar_s(av[i]); | 2617 | as[i] = getvar_s(av[i]); |
2618 | } | ||
2584 | isr >>= 1; | 2619 | isr >>= 1; |
2585 | } | 2620 | } |
2586 | 2621 | ||
@@ -2931,7 +2966,6 @@ static var *evaluate(node *op, var *res) | |||
2931 | debug_printf_eval("PRINTF\n"); | 2966 | debug_printf_eval("PRINTF\n"); |
2932 | { | 2967 | { |
2933 | FILE *F = stdout; | 2968 | FILE *F = stdout; |
2934 | IF_FEATURE_AWK_GNU_EXTENSIONS(int len;) | ||
2935 | 2969 | ||
2936 | if (op->r.n) { | 2970 | if (op->r.n) { |
2937 | rstream *rsm = newfile(R.s); | 2971 | rstream *rsm = newfile(R.s); |
@@ -2948,15 +2982,18 @@ static var *evaluate(node *op, var *res) | |||
2948 | F = rsm->F; | 2982 | F = rsm->F; |
2949 | } | 2983 | } |
2950 | 2984 | ||
2951 | if (opinfo == TI_PRINT) { | 2985 | /* Can't just check 'opinfo == OC_PRINT' here, parser ORs |
2986 | * additional bits to opinfos of print/printf with redirects | ||
2987 | */ | ||
2988 | if ((opinfo & OPCLSMASK) == OC_PRINT) { | ||
2952 | if (!op1) { | 2989 | if (!op1) { |
2953 | fputs(getvar_s(intvar[F0]), F); | 2990 | fputs(getvar_s(intvar[F0]), F); |
2954 | } else { | 2991 | } else { |
2955 | for (;;) { | 2992 | for (;;) { |
2956 | var *v = evaluate(nextarg(&op1), TMPVAR0); | 2993 | var *v = evaluate(nextarg(&op1), TMPVAR0); |
2957 | if (v->type & VF_NUMBER) { | 2994 | if (v->type & VF_NUMBER) { |
2958 | fmt_num(g_buf, MAXVARFMT, getvar_s(intvar[OFMT]), | 2995 | fmt_num(getvar_s(intvar[OFMT]), |
2959 | getvar_i(v), TRUE); | 2996 | getvar_i(v)); |
2960 | fputs(g_buf, F); | 2997 | fputs(g_buf, F); |
2961 | } else { | 2998 | } else { |
2962 | fputs(getvar_s(v), F); | 2999 | fputs(getvar_s(v), F); |
@@ -2968,6 +3005,7 @@ static var *evaluate(node *op, var *res) | |||
2968 | } | 3005 | } |
2969 | fputs(getvar_s(intvar[ORS]), F); | 3006 | fputs(getvar_s(intvar[ORS]), F); |
2970 | } else { /* PRINTF */ | 3007 | } else { /* PRINTF */ |
3008 | IF_FEATURE_AWK_GNU_EXTENSIONS(size_t len;) | ||
2971 | char *s = awk_printf(op1, &len); | 3009 | char *s = awk_printf(op1, &len); |
2972 | #if ENABLE_FEATURE_AWK_GNU_EXTENSIONS | 3010 | #if ENABLE_FEATURE_AWK_GNU_EXTENSIONS |
2973 | fwrite(s, len, 1, F); | 3011 | fwrite(s, len, 1, F); |
@@ -3149,7 +3187,8 @@ static var *evaluate(node *op, var *res) | |||
3149 | if (op1) { | 3187 | if (op1) { |
3150 | rsm = newfile(L.s); | 3188 | rsm = newfile(L.s); |
3151 | if (!rsm->F) { | 3189 | if (!rsm->F) { |
3152 | if (opinfo == TI_PGETLINE) { | 3190 | /* NB: can't use "opinfo == TI_PGETLINE", would break "cmd" | getline */ |
3191 | if ((opinfo & OPCLSMASK) == OC_PGETLINE) { | ||
3153 | rsm->F = popen(L.s, "r"); | 3192 | rsm->F = popen(L.s, "r"); |
3154 | rsm->is_pipe = TRUE; | 3193 | rsm->is_pipe = TRUE; |
3155 | } else { | 3194 | } else { |
@@ -3496,7 +3535,6 @@ static var *evaluate(node *op, var *res) | |||
3496 | #undef sreg | 3535 | #undef sreg |
3497 | } | 3536 | } |
3498 | 3537 | ||
3499 | |||
3500 | /* -------- main & co. -------- */ | 3538 | /* -------- main & co. -------- */ |
3501 | 3539 | ||
3502 | static int awk_exit(void) | 3540 | static int awk_exit(void) |
diff --git a/editors/vi.c b/editors/vi.c index 89c567f10..a079be3fe 100644 --- a/editors/vi.c +++ b/editors/vi.c | |||
@@ -2444,12 +2444,14 @@ static char *char_search(char *p, const char *pat, int dir_and_range) | |||
2444 | char *q; | 2444 | char *q; |
2445 | int i, size, range, start; | 2445 | int i, size, range, start; |
2446 | 2446 | ||
2447 | re_syntax_options = RE_SYNTAX_POSIX_EXTENDED; | 2447 | re_syntax_options = RE_SYNTAX_POSIX_BASIC & (~RE_DOT_NEWLINE); |
2448 | if (ignorecase) | 2448 | if (ignorecase) |
2449 | re_syntax_options = RE_SYNTAX_POSIX_EXTENDED | RE_ICASE; | 2449 | re_syntax_options |= RE_ICASE; |
2450 | 2450 | ||
2451 | memset(&preg, 0, sizeof(preg)); | 2451 | memset(&preg, 0, sizeof(preg)); |
2452 | err = re_compile_pattern(pat, strlen(pat), &preg); | 2452 | err = re_compile_pattern(pat, strlen(pat), &preg); |
2453 | preg.not_bol = p != text; | ||
2454 | preg.not_eol = p != end - 1; | ||
2453 | if (err != NULL) { | 2455 | if (err != NULL) { |
2454 | status_line_bold("bad search pattern '%s': %s", pat, err); | 2456 | status_line_bold("bad search pattern '%s': %s", pat, err); |
2455 | return p; | 2457 | return p; |
@@ -2741,6 +2743,75 @@ static char *expand_args(char *args) | |||
2741 | # endif | 2743 | # endif |
2742 | #endif /* FEATURE_VI_COLON */ | 2744 | #endif /* FEATURE_VI_COLON */ |
2743 | 2745 | ||
2746 | #if ENABLE_FEATURE_VI_REGEX_SEARCH | ||
2747 | # define MAX_SUBPATTERN 10 // subpatterns \0 .. \9 | ||
2748 | |||
2749 | // Like strchr() but skipping backslash-escaped characters | ||
2750 | static char *strchr_backslash(const char *s, int c) | ||
2751 | { | ||
2752 | while (*s) { | ||
2753 | if (*s == c) | ||
2754 | return (char *)s; | ||
2755 | if (*s == '\\') | ||
2756 | if (*++s == '\0') | ||
2757 | break; | ||
2758 | s++; | ||
2759 | } | ||
2760 | return NULL; | ||
2761 | } | ||
2762 | |||
2763 | // If the return value is not NULL the caller should free R | ||
2764 | static char *regex_search(char *q, regex_t *preg, const char *Rorig, | ||
2765 | size_t *len_F, size_t *len_R, char **R) | ||
2766 | { | ||
2767 | regmatch_t regmatch[MAX_SUBPATTERN], *cur_match; | ||
2768 | char *found = NULL; | ||
2769 | const char *t; | ||
2770 | char *r; | ||
2771 | |||
2772 | regmatch[0].rm_so = 0; | ||
2773 | regmatch[0].rm_eo = end_line(q) - q; | ||
2774 | if (regexec(preg, q, MAX_SUBPATTERN, regmatch, REG_STARTEND) != 0) | ||
2775 | return found; | ||
2776 | |||
2777 | found = q + regmatch[0].rm_so; | ||
2778 | *len_F = regmatch[0].rm_eo - regmatch[0].rm_so; | ||
2779 | *R = NULL; | ||
2780 | |||
2781 | fill_result: | ||
2782 | // first pass calculates len_R, second fills R | ||
2783 | *len_R = 0; | ||
2784 | for (t = Rorig, r = *R; *t; t++) { | ||
2785 | size_t len = 1; // default is to copy one char from replace pattern | ||
2786 | const char *from = t; | ||
2787 | if (*t == '\\') { | ||
2788 | from = ++t; // skip backslash | ||
2789 | if (*t >= '0' && *t < '0' + MAX_SUBPATTERN) { | ||
2790 | cur_match = regmatch + (*t - '0'); | ||
2791 | if (cur_match->rm_so >= 0) { | ||
2792 | len = cur_match->rm_eo - cur_match->rm_so; | ||
2793 | from = q + cur_match->rm_so; | ||
2794 | } | ||
2795 | } | ||
2796 | } | ||
2797 | *len_R += len; | ||
2798 | if (*R) { | ||
2799 | memcpy(r, from, len); | ||
2800 | r += len; | ||
2801 | /* *r = '\0'; - xzalloc did it */ | ||
2802 | } | ||
2803 | } | ||
2804 | if (*R == NULL) { | ||
2805 | *R = xzalloc(*len_R + 1); | ||
2806 | goto fill_result; | ||
2807 | } | ||
2808 | |||
2809 | return found; | ||
2810 | } | ||
2811 | #else /* !ENABLE_FEATURE_VI_REGEX_SEARCH */ | ||
2812 | # define strchr_backslash(s, c) strchr(s, c) | ||
2813 | #endif /* ENABLE_FEATURE_VI_REGEX_SEARCH */ | ||
2814 | |||
2744 | // buf must be no longer than MAX_INPUT_LEN! | 2815 | // buf must be no longer than MAX_INPUT_LEN! |
2745 | static void colon(char *buf) | 2816 | static void colon(char *buf) |
2746 | { | 2817 | { |
@@ -3148,23 +3219,30 @@ static void colon(char *buf) | |||
3148 | # if ENABLE_FEATURE_VI_VERBOSE_STATUS | 3219 | # if ENABLE_FEATURE_VI_VERBOSE_STATUS |
3149 | int last_line = 0, lines = 0; | 3220 | int last_line = 0, lines = 0; |
3150 | # endif | 3221 | # endif |
3222 | # if ENABLE_FEATURE_VI_REGEX_SEARCH | ||
3223 | regex_t preg; | ||
3224 | int cflags; | ||
3225 | char *Rorig; | ||
3226 | # if ENABLE_FEATURE_VI_UNDO | ||
3227 | int undo = 0; | ||
3228 | # endif | ||
3229 | # endif | ||
3151 | 3230 | ||
3152 | // F points to the "find" pattern | 3231 | // F points to the "find" pattern |
3153 | // R points to the "replace" pattern | 3232 | // R points to the "replace" pattern |
3154 | // replace the cmd line delimiters "/" with NULs | 3233 | // replace the cmd line delimiters "/" with NULs |
3155 | c = buf[1]; // what is the delimiter | 3234 | c = buf[1]; // what is the delimiter |
3156 | F = buf + 2; // start of "find" | 3235 | F = buf + 2; // start of "find" |
3157 | R = strchr(F, c); // middle delimiter | 3236 | R = strchr_backslash(F, c); // middle delimiter |
3158 | if (!R) | 3237 | if (!R) |
3159 | goto colon_s_fail; | 3238 | goto colon_s_fail; |
3160 | len_F = R - F; | 3239 | len_F = R - F; |
3161 | *R++ = '\0'; // terminate "find" | 3240 | *R++ = '\0'; // terminate "find" |
3162 | flags = strchr(R, c); | 3241 | flags = strchr_backslash(R, c); |
3163 | if (flags) { | 3242 | if (flags) { |
3164 | *flags++ = '\0'; // terminate "replace" | 3243 | *flags++ = '\0'; // terminate "replace" |
3165 | gflag = *flags; | 3244 | gflag = *flags; |
3166 | } | 3245 | } |
3167 | len_R = strlen(R); | ||
3168 | 3246 | ||
3169 | if (len_F) { // save "find" as last search pattern | 3247 | if (len_F) { // save "find" as last search pattern |
3170 | free(last_search_pattern); | 3248 | free(last_search_pattern); |
@@ -3186,31 +3264,67 @@ static void colon(char *buf) | |||
3186 | b = e; | 3264 | b = e; |
3187 | } | 3265 | } |
3188 | 3266 | ||
3267 | # if ENABLE_FEATURE_VI_REGEX_SEARCH | ||
3268 | Rorig = R; | ||
3269 | cflags = 0; | ||
3270 | if (ignorecase) | ||
3271 | cflags = REG_ICASE; | ||
3272 | memset(&preg, 0, sizeof(preg)); | ||
3273 | if (regcomp(&preg, F, cflags) != 0) { | ||
3274 | status_line(":s bad search pattern"); | ||
3275 | goto regex_search_end; | ||
3276 | } | ||
3277 | # else | ||
3278 | len_R = strlen(R); | ||
3279 | # endif | ||
3280 | |||
3189 | for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0 | 3281 | for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0 |
3190 | char *ls = q; // orig line start | 3282 | char *ls = q; // orig line start |
3191 | char *found; | 3283 | char *found; |
3192 | vc4: | 3284 | vc4: |
3285 | # if ENABLE_FEATURE_VI_REGEX_SEARCH | ||
3286 | found = regex_search(q, &preg, Rorig, &len_F, &len_R, &R); | ||
3287 | # else | ||
3193 | found = char_search(q, F, (FORWARD << 1) | LIMITED); // search cur line only for "find" | 3288 | found = char_search(q, F, (FORWARD << 1) | LIMITED); // search cur line only for "find" |
3289 | # endif | ||
3194 | if (found) { | 3290 | if (found) { |
3195 | uintptr_t bias; | 3291 | uintptr_t bias; |
3196 | // we found the "find" pattern - delete it | 3292 | // we found the "find" pattern - delete it |
3197 | // For undo support, the first item should not be chained | 3293 | // For undo support, the first item should not be chained |
3198 | text_hole_delete(found, found + len_F - 1, | 3294 | // This needs to be handled differently depending on |
3199 | subs ? ALLOW_UNDO_CHAIN: ALLOW_UNDO); | 3295 | // whether or not regex support is enabled. |
3200 | // can't do this above, no undo => no third argument | 3296 | # if ENABLE_FEATURE_VI_REGEX_SEARCH |
3201 | subs++; | 3297 | # define TEST_LEN_F len_F // len_F may be zero |
3202 | # if ENABLE_FEATURE_VI_VERBOSE_STATUS | 3298 | # define TEST_UNDO1 undo++ |
3203 | if (last_line != i) { | 3299 | # define TEST_UNDO2 undo++ |
3204 | last_line = i; | 3300 | # else |
3205 | ++lines; | 3301 | # define TEST_LEN_F 1 // len_F is never zero |
3302 | # define TEST_UNDO1 subs | ||
3303 | # define TEST_UNDO2 1 | ||
3304 | # endif | ||
3305 | if (TEST_LEN_F) // match can be empty, no delete needed | ||
3306 | text_hole_delete(found, found + len_F - 1, | ||
3307 | TEST_UNDO1 ? ALLOW_UNDO_CHAIN : ALLOW_UNDO); | ||
3308 | if (len_R != 0) { // insert the "replace" pattern, if required | ||
3309 | bias = string_insert(found, R, | ||
3310 | TEST_UNDO2 ? ALLOW_UNDO_CHAIN : ALLOW_UNDO); | ||
3311 | found += bias; | ||
3312 | ls += bias; | ||
3313 | //q += bias; - recalculated anyway | ||
3206 | } | 3314 | } |
3315 | # if ENABLE_FEATURE_VI_REGEX_SEARCH | ||
3316 | free(R); | ||
3317 | # endif | ||
3318 | if (TEST_LEN_F || len_R != 0) { | ||
3319 | dot = ls; | ||
3320 | subs++; | ||
3321 | # if ENABLE_FEATURE_VI_VERBOSE_STATUS | ||
3322 | if (last_line != i) { | ||
3323 | last_line = i; | ||
3324 | ++lines; | ||
3325 | } | ||
3207 | # endif | 3326 | # endif |
3208 | // insert the "replace" patern | 3327 | } |
3209 | bias = string_insert(found, R, ALLOW_UNDO_CHAIN); | ||
3210 | found += bias; | ||
3211 | ls += bias; | ||
3212 | dot = ls; | ||
3213 | //q += bias; - recalculated anyway | ||
3214 | // check for "global" :s/foo/bar/g | 3328 | // check for "global" :s/foo/bar/g |
3215 | if (gflag == 'g') { | 3329 | if (gflag == 'g') { |
3216 | if ((found + len_R) < end_line(ls)) { | 3330 | if ((found + len_R) < end_line(ls)) { |
@@ -3230,6 +3344,10 @@ static void colon(char *buf) | |||
3230 | status_line("%d substitutions on %d lines", subs, lines); | 3344 | status_line("%d substitutions on %d lines", subs, lines); |
3231 | # endif | 3345 | # endif |
3232 | } | 3346 | } |
3347 | # if ENABLE_FEATURE_VI_REGEX_SEARCH | ||
3348 | regex_search_end: | ||
3349 | regfree(&preg); | ||
3350 | # endif | ||
3233 | # endif /* FEATURE_VI_SEARCH */ | 3351 | # endif /* FEATURE_VI_SEARCH */ |
3234 | } else if (strncmp(cmd, "version", i) == 0) { // show software version | 3352 | } else if (strncmp(cmd, "version", i) == 0) { // show software version |
3235 | status_line(BB_VER); | 3353 | status_line(BB_VER); |
@@ -3503,7 +3621,7 @@ static int find_range(char **start, char **stop, int cmd) | |||
3503 | // for non-change operations WS after NL is not part of word | 3621 | // for non-change operations WS after NL is not part of word |
3504 | if (cmd != 'c' && dot != t && *dot != '\n') | 3622 | if (cmd != 'c' && dot != t && *dot != '\n') |
3505 | dot = t; | 3623 | dot = t; |
3506 | } else if (strchr("GHL+-jk'\r\n", c)) { | 3624 | } else if (strchr("GHL+-gjk'\r\n", c)) { |
3507 | // these operate on whole lines | 3625 | // these operate on whole lines |
3508 | buftype = WHOLE; | 3626 | buftype = WHOLE; |
3509 | do_cmd(c); // execute movement cmd | 3627 | do_cmd(c); // execute movement cmd |
@@ -4093,6 +4211,7 @@ static void do_cmd(int c) | |||
4093 | buf[1] = (c1 >= 0 ? c1 : '*'); | 4211 | buf[1] = (c1 >= 0 ? c1 : '*'); |
4094 | buf[2] = '\0'; | 4212 | buf[2] = '\0'; |
4095 | not_implemented(buf); | 4213 | not_implemented(buf); |
4214 | cmd_error = TRUE; | ||
4096 | break; | 4215 | break; |
4097 | } | 4216 | } |
4098 | if (cmdcnt == 0) | 4217 | if (cmdcnt == 0) |
diff --git a/networking/tc.c b/networking/tc.c index 510684443..46ad23d8b 100644 --- a/networking/tc.c +++ b/networking/tc.c | |||
@@ -111,16 +111,14 @@ static char* print_tc_classid(uint32_t cid) | |||
111 | #if 0 /* IMPOSSIBLE */ | 111 | #if 0 /* IMPOSSIBLE */ |
112 | if (cid == TC_H_ROOT) | 112 | if (cid == TC_H_ROOT) |
113 | return xasprintf("root"); | 113 | return xasprintf("root"); |
114 | else | ||
115 | #endif | 114 | #endif |
116 | if (cid == TC_H_UNSPEC) | 115 | if (cid == TC_H_UNSPEC) |
117 | return xasprintf("none"); | 116 | return xasprintf("none"); |
118 | else if (TC_H_MAJ(cid) == 0) | 117 | if (TC_H_MAJ(cid) == 0) |
119 | return xasprintf(":%x", TC_H_MIN(cid)); | 118 | return xasprintf(":%x", TC_H_MIN(cid)); |
120 | else if (TC_H_MIN(cid) == 0) | 119 | if (TC_H_MIN(cid) == 0) |
121 | return xasprintf("%x:", TC_H_MAJ(cid)>>16); | 120 | return xasprintf("%x:", TC_H_MAJ(cid)>>16); |
122 | else | 121 | return xasprintf("%x:%x", TC_H_MAJ(cid)>>16, TC_H_MIN(cid)); |
123 | return xasprintf("%x:%x", TC_H_MAJ(cid)>>16, TC_H_MIN(cid)); | ||
124 | } | 122 | } |
125 | 123 | ||
126 | /* Get a qdisc handle. Return 0 on success, !0 otherwise. */ | 124 | /* Get a qdisc handle. Return 0 on success, !0 otherwise. */ |
@@ -376,8 +374,10 @@ static FAST_FUNC int print_qdisc( | |||
376 | prio_print_opt(tb[TCA_OPTIONS]); | 374 | prio_print_opt(tb[TCA_OPTIONS]); |
377 | } else if (qqq == 1) { /* class based queuing */ | 375 | } else if (qqq == 1) { /* class based queuing */ |
378 | cbq_print_opt(tb[TCA_OPTIONS]); | 376 | cbq_print_opt(tb[TCA_OPTIONS]); |
379 | } else | 377 | } else { |
380 | bb_error_msg("unknown %s", name); | 378 | /* don't know how to print options for this qdisc */ |
379 | printf("(options for %s)", name); | ||
380 | } | ||
381 | } | 381 | } |
382 | bb_putchar('\n'); | 382 | bb_putchar('\n'); |
383 | return 0; | 383 | return 0; |
@@ -405,7 +405,7 @@ static FAST_FUNC int print_class( | |||
405 | return -1; | 405 | return -1; |
406 | } | 406 | } |
407 | /* not the desired interface? */ | 407 | /* not the desired interface? */ |
408 | if (filter_qdisc && TC_H_MAJ(msg->tcm_handle^filter_qdisc)) | 408 | if (filter_qdisc && TC_H_MAJ(msg->tcm_handle ^ filter_qdisc)) |
409 | return 0; | 409 | return 0; |
410 | memset (tb, 0, sizeof(tb)); | 410 | memset (tb, 0, sizeof(tb)); |
411 | parse_rtattr(tb, TCA_MAX, TCA_RTA(msg), len); | 411 | parse_rtattr(tb, TCA_MAX, TCA_RTA(msg), len); |
@@ -418,8 +418,8 @@ static FAST_FUNC int print_class( | |||
418 | 418 | ||
419 | name = (char*)RTA_DATA(tb[TCA_KIND]); | 419 | name = (char*)RTA_DATA(tb[TCA_KIND]); |
420 | classid = !msg->tcm_handle ? NULL : print_tc_classid( | 420 | classid = !msg->tcm_handle ? NULL : print_tc_classid( |
421 | filter_qdisc ? TC_H_MIN(msg->tcm_parent) : msg->tcm_parent); | 421 | filter_qdisc ? TC_H_MIN(msg->tcm_handle) : msg->tcm_handle); |
422 | printf ("class %s %s", name, classid); | 422 | printf ("class %s %s ", name, classid); |
423 | if (ENABLE_FEATURE_CLEAN_UP) | 423 | if (ENABLE_FEATURE_CLEAN_UP) |
424 | free(classid); | 424 | free(classid); |
425 | 425 | ||
@@ -445,8 +445,10 @@ static FAST_FUNC int print_class( | |||
445 | } else if (qqq == 1) { /* class based queuing */ | 445 | } else if (qqq == 1) { /* class based queuing */ |
446 | /* cbq_print_copt() is identical to cbq_print_opt(). */ | 446 | /* cbq_print_copt() is identical to cbq_print_opt(). */ |
447 | cbq_print_opt(tb[TCA_OPTIONS]); | 447 | cbq_print_opt(tb[TCA_OPTIONS]); |
448 | } else | 448 | } else { |
449 | bb_error_msg("unknown %s", name); | 449 | /* don't know how to print options for this class */ |
450 | printf("(options for %s)", name); | ||
451 | } | ||
450 | } | 452 | } |
451 | bb_putchar('\n'); | 453 | bb_putchar('\n'); |
452 | 454 | ||
@@ -511,12 +513,11 @@ int tc_main(int argc UNUSED_PARAM, char **argv) | |||
511 | ret = EXIT_SUCCESS; | 513 | ret = EXIT_SUCCESS; |
512 | 514 | ||
513 | obj = index_in_substrings(objects, *argv++); | 515 | obj = index_in_substrings(objects, *argv++); |
514 | |||
515 | if (obj < 0) | 516 | if (obj < 0) |
516 | bb_show_usage(); | 517 | bb_show_usage(); |
517 | if (!*argv) | 518 | |
518 | cmd = CMD_show; /* list is the default */ | 519 | cmd = CMD_show; /* list (aka show) is the default */ |
519 | else { | 520 | if (*argv) { |
520 | cmd = index_in_substrings(commands, *argv); | 521 | cmd = index_in_substrings(commands, *argv); |
521 | if (cmd < 0) | 522 | if (cmd < 0) |
522 | invarg_1_to_2(*argv, argv[-1]); | 523 | invarg_1_to_2(*argv, argv[-1]); |
@@ -538,16 +539,17 @@ int tc_main(int argc UNUSED_PARAM, char **argv) | |||
538 | msg.tcm_ifindex = xll_name_to_index(dev); | 539 | msg.tcm_ifindex = xll_name_to_index(dev); |
539 | if (cmd >= CMD_show) | 540 | if (cmd >= CMD_show) |
540 | filter_ifindex = msg.tcm_ifindex; | 541 | filter_ifindex = msg.tcm_ifindex; |
541 | } else | 542 | continue; |
542 | if ((arg == ARG_qdisc && obj == OBJ_class && cmd >= CMD_show) | 543 | } |
543 | || (arg == ARG_handle && obj == OBJ_qdisc && cmd == CMD_change) | 544 | if ((arg == ARG_qdisc && obj == OBJ_class && cmd >= CMD_show) /* tc class show|list qdisc HANDLE */ |
545 | || (arg == ARG_handle && obj == OBJ_qdisc && cmd == CMD_change) /* tc qdisc change handle HANDLE */ | ||
544 | ) { | 546 | ) { |
545 | NEXT_ARG(); | 547 | NEXT_ARG(); |
546 | /* We don't care about duparg2("qdisc handle",*argv) for now */ | 548 | /* We don't care about duparg2("qdisc handle",*argv) for now */ |
547 | if (get_qdisc_handle(&filter_qdisc, *argv)) | 549 | if (get_qdisc_handle(&filter_qdisc, *argv)) |
548 | invarg_1_to_2(*argv, "qdisc"); | 550 | invarg_1_to_2(*argv, "qdisc"); |
549 | } else | 551 | } else |
550 | if (obj != OBJ_qdisc | 552 | if (obj != OBJ_qdisc /* tc class|filter root|parent | tc filter preference|priority|protocol */ |
551 | && (arg == ARG_root | 553 | && (arg == ARG_root |
552 | || arg == ARG_parent | 554 | || arg == ARG_parent |
553 | || (obj == OBJ_filter && arg >= ARG_pref) | 555 | || (obj == OBJ_filter && arg >= ARG_pref) |
diff --git a/testsuite/awk.tests b/testsuite/awk.tests index 770d8ffce..f53b1efe2 100755 --- a/testsuite/awk.tests +++ b/testsuite/awk.tests | |||
@@ -379,19 +379,14 @@ testing "awk -e and ARGC" \ | |||
379 | "" | 379 | "" |
380 | SKIP= | 380 | SKIP= |
381 | 381 | ||
382 | # The examples are in fact not valid awk programs (break/continue | ||
383 | # can only be used inside loops). | ||
384 | # But we do accept them outside of loops. | ||
385 | # We had a bug with misparsing "break ; else" sequence. | ||
386 | # Test that *that* bug is fixed, using simplest possible scripts: | ||
387 | testing "awk break" \ | 382 | testing "awk break" \ |
388 | "awk -f - 2>&1; echo \$?" \ | 383 | "awk -f - 2>&1; echo \$?" \ |
389 | "0\n" \ | 384 | "awk: -:1: 'break' not in a loop\n1\n" \ |
390 | "" \ | 385 | "" \ |
391 | 'BEGIN { if (1) break; else a = 1 }' | 386 | 'BEGIN { if (1) break; else a = 1 }' |
392 | testing "awk continue" \ | 387 | testing "awk continue" \ |
393 | "awk -f - 2>&1; echo \$?" \ | 388 | "awk -f - 2>&1; echo \$?" \ |
394 | "0\n" \ | 389 | "awk: -:1: 'continue' not in a loop\n1\n" \ |
395 | "" \ | 390 | "" \ |
396 | 'BEGIN { if (1) continue; else a = 1 }' | 391 | 'BEGIN { if (1) continue; else a = 1 }' |
397 | 392 | ||
@@ -415,6 +410,14 @@ testing "awk printf('%c') can output NUL" \ | |||
415 | "awk '{printf(\"hello%c null\n\", 0)}'" "hello\0 null\n" "" "\n" | 410 | "awk '{printf(\"hello%c null\n\", 0)}'" "hello\0 null\n" "" "\n" |
416 | SKIP= | 411 | SKIP= |
417 | 412 | ||
413 | optional FEATURE_AWK_GNU_EXTENSIONS | ||
414 | testing "awk printf('%-10c') can output NUL" \ | ||
415 | "awk 'BEGIN { printf \"[%-10c]\n\", 0 }' | od -tx1" "\ | ||
416 | 0000000 5b 00 20 20 20 20 20 20 20 20 20 5d 0a | ||
417 | 0000015 | ||
418 | " "" "" | ||
419 | SKIP= | ||
420 | |||
418 | # testing "description" "command" "result" "infile" "stdin" | 421 | # testing "description" "command" "result" "infile" "stdin" |
419 | testing 'awk negative field access' \ | 422 | testing 'awk negative field access' \ |
420 | 'awk 2>&1 -- '\''{ $(-1) }'\' \ | 423 | 'awk 2>&1 -- '\''{ $(-1) }'\' \ |
@@ -450,4 +453,14 @@ testing "awk exit N propagates through END's exit" \ | |||
450 | "42\n" \ | 453 | "42\n" \ |
451 | '' '' | 454 | '' '' |
452 | 455 | ||
456 | testing "awk print + redirect" \ | ||
457 | "awk 'BEGIN { print \"STDERR %s\" >\"/dev/stderr\" }' 2>&1" \ | ||
458 | "STDERR %s\n" \ | ||
459 | '' '' | ||
460 | |||
461 | testing "awk \"cmd\" | getline" \ | ||
462 | "awk 'BEGIN { \"echo HELLO\" | getline; print }'" \ | ||
463 | "HELLO\n" \ | ||
464 | '' '' | ||
465 | |||
453 | exit $FAILCOUNT | 466 | exit $FAILCOUNT |