aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2021-07-17 10:35:24 +0100
committerRon Yorston <rmy@pobox.com>2021-07-17 10:35:24 +0100
commit36e9606c1d1edfbc9e4eba31b4c498dba361ac98 (patch)
tree492a436d5fba5a545e7155a115679e0669e9bed6
parentdcca23dc4cf9b51a1c2576360fea190e9b71204e (diff)
parentdabbeeb79356eef78528acd55e1f143ae80372f7 (diff)
downloadbusybox-w32-36e9606c1d1edfbc9e4eba31b4c498dba361ac98.tar.gz
busybox-w32-36e9606c1d1edfbc9e4eba31b4c498dba361ac98.tar.bz2
busybox-w32-36e9606c1d1edfbc9e4eba31b4c498dba361ac98.zip
Merge branch 'busybox' into merge
-rw-r--r--editors/awk.c316
-rw-r--r--editors/vi.c159
-rw-r--r--networking/tc.c42
-rwxr-xr-xtestsuite/awk.tests27
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 { \ 240static void debug_parse_print_tc(uint32_t n)
241if ((n) & TC_LPAREN ) debug_printf_parse(" LPAREN" ); \ 241{
242if ((n) & TC_RPAREN ) debug_printf_parse(" RPAREN" ); \ 242 if (n & TC_LPAREN ) debug_printf_parse(" LPAREN" );
243if ((n) & TC_REGEXP ) debug_printf_parse(" REGEXP" ); \ 243 if (n & TC_RPAREN ) debug_printf_parse(" RPAREN" );
244if ((n) & TC_OUTRDR ) debug_printf_parse(" OUTRDR" ); \ 244 if (n & TC_REGEXP ) debug_printf_parse(" REGEXP" );
245if ((n) & TC_UOPPOST ) debug_printf_parse(" UOPPOST" ); \ 245 if (n & TC_OUTRDR ) debug_printf_parse(" OUTRDR" );
246if ((n) & TC_UOPPRE1 ) debug_printf_parse(" UOPPRE1" ); \ 246 if (n & TC_UOPPOST ) debug_printf_parse(" UOPPOST" );
247if ((n) & TC_BINOPX ) debug_printf_parse(" BINOPX" ); \ 247 if (n & TC_UOPPRE1 ) debug_printf_parse(" UOPPRE1" );
248if ((n) & TC_IN ) debug_printf_parse(" IN" ); \ 248 if (n & TC_BINOPX ) debug_printf_parse(" BINOPX" );
249if ((n) & TC_COMMA ) debug_printf_parse(" COMMA" ); \ 249 if (n & TC_IN ) debug_printf_parse(" IN" );
250if ((n) & TC_PIPE ) debug_printf_parse(" PIPE" ); \ 250 if (n & TC_COMMA ) debug_printf_parse(" COMMA" );
251if ((n) & TC_UOPPRE2 ) debug_printf_parse(" UOPPRE2" ); \ 251 if (n & TC_PIPE ) debug_printf_parse(" PIPE" );
252if ((n) & TC_ARRTERM ) debug_printf_parse(" ARRTERM" ); \ 252 if (n & TC_UOPPRE2 ) debug_printf_parse(" UOPPRE2" );
253if ((n) & TC_LBRACE ) debug_printf_parse(" LBRACE" ); \ 253 if (n & TC_ARRTERM ) debug_printf_parse(" ARRTERM" );
254if ((n) & TC_RBRACE ) debug_printf_parse(" RBRACE" ); \ 254 if (n & TC_LBRACE ) debug_printf_parse(" LBRACE" );
255if ((n) & TC_SEMICOL ) debug_printf_parse(" SEMICOL" ); \ 255 if (n & TC_RBRACE ) debug_printf_parse(" RBRACE" );
256if ((n) & TC_NEWLINE ) debug_printf_parse(" NEWLINE" ); \ 256 if (n & TC_SEMICOL ) debug_printf_parse(" SEMICOL" );
257if ((n) & TC_STATX ) debug_printf_parse(" STATX" ); \ 257 if (n & TC_NEWLINE ) debug_printf_parse(" NEWLINE" );
258if ((n) & TC_WHILE ) debug_printf_parse(" WHILE" ); \ 258 if (n & TC_STATX ) debug_printf_parse(" STATX" );
259if ((n) & TC_ELSE ) debug_printf_parse(" ELSE" ); \ 259 if (n & TC_WHILE ) debug_printf_parse(" WHILE" );
260if ((n) & TC_BUILTIN ) debug_printf_parse(" BUILTIN" ); \ 260 if (n & TC_ELSE ) debug_printf_parse(" ELSE" );
261if ((n) & TC_LENGTH ) debug_printf_parse(" LENGTH" ); \ 261 if (n & TC_BUILTIN ) debug_printf_parse(" BUILTIN" );
262if ((n) & TC_GETLINE ) debug_printf_parse(" GETLINE" ); \ 262 if (n & TC_LENGTH ) debug_printf_parse(" LENGTH" );
263if ((n) & TC_FUNCDECL) debug_printf_parse(" FUNCDECL"); \ 263 if (n & TC_GETLINE ) debug_printf_parse(" GETLINE" );
264if ((n) & TC_BEGIN ) debug_printf_parse(" BEGIN" ); \ 264 if (n & TC_FUNCDECL) debug_printf_parse(" FUNCDECL");
265if ((n) & TC_END ) debug_printf_parse(" END" ); \ 265 if (n & TC_BEGIN ) debug_printf_parse(" BEGIN" );
266if ((n) & TC_EOF ) debug_printf_parse(" EOF" ); \ 266 if (n & TC_END ) debug_printf_parse(" END" );
267if ((n) & TC_VARIABLE) debug_printf_parse(" VARIABLE"); \ 267 if (n & TC_EOF ) debug_printf_parse(" EOF" );
268if ((n) & TC_ARRAY ) debug_printf_parse(" ARRAY" ); \ 268 if (n & TC_VARIABLE) debug_printf_parse(" VARIABLE");
269if ((n) & TC_FUNCTION) debug_printf_parse(" FUNCTION"); \ 269 if (n & TC_ARRAY ) debug_printf_parse(" ARRAY" );
270if ((n) & TC_STRING ) debug_printf_parse(" STRING" ); \ 270 if (n & TC_FUNCTION) debug_printf_parse(" FUNCTION");
271if ((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
911static int fmt_num(char *b, int size, const char *format, double n, int int_as_int) 911static 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
932static xhash *iamarray(var *a) 930static 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
2335static char *awk_printf(node *n, int *len) 2341static 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
3502static int awk_exit(void) 3540static 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
2750static 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
2764static 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!
2745static void colon(char *buf) 2816static 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 ""
380SKIP= 380SKIP=
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:
387testing "awk break" \ 382testing "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 }'
392testing "awk continue" \ 387testing "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"
416SKIP= 411SKIP=
417 412
413optional FEATURE_AWK_GNU_EXTENSIONS
414testing "awk printf('%-10c') can output NUL" \
415 "awk 'BEGIN { printf \"[%-10c]\n\", 0 }' | od -tx1" "\
4160000000 5b 00 20 20 20 20 20 20 20 20 20 5d 0a
4170000015
418" "" ""
419SKIP=
420
418# testing "description" "command" "result" "infile" "stdin" 421# testing "description" "command" "result" "infile" "stdin"
419testing 'awk negative field access' \ 422testing '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
456testing "awk print + redirect" \
457 "awk 'BEGIN { print \"STDERR %s\" >\"/dev/stderr\" }' 2>&1" \
458 "STDERR %s\n" \
459 '' ''
460
461testing "awk \"cmd\" | getline" \
462 "awk 'BEGIN { \"echo HELLO\" | getline; print }'" \
463 "HELLO\n" \
464 '' ''
465
453exit $FAILCOUNT 466exit $FAILCOUNT