aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2023-06-16 13:02:47 +0100
committerRon Yorston <rmy@pobox.com>2023-06-16 13:02:47 +0100
commit4b3d7e62cff6015e1c4b856165efd90cf9182a5c (patch)
tree0a59c6a94a47c3e8f1a47b0a3218f9591b842385
parent0627e352656effac8d8e617378e7a68edfce41df (diff)
parent2ca39ffd447ca874fcea933194829717d5573247 (diff)
downloadbusybox-w32-4b3d7e62cff6015e1c4b856165efd90cf9182a5c.tar.gz
busybox-w32-4b3d7e62cff6015e1c4b856165efd90cf9182a5c.tar.bz2
busybox-w32-4b3d7e62cff6015e1c4b856165efd90cf9182a5c.zip
Merge branch 'busybox' into merge
-rw-r--r--coreutils/od.c73
-rw-r--r--coreutils/od_bloaty.c56
-rw-r--r--editors/awk.c409
-rw-r--r--include/dump.h7
-rw-r--r--libbb/dump.c102
-rw-r--r--networking/tunctl.c3
-rw-r--r--shell/ash_test/ash-misc/elif1.right4
-rwxr-xr-xshell/ash_test/ash-misc/elif1.tests6
-rw-r--r--shell/ash_test/ash-misc/elif2.right2
-rwxr-xr-xshell/ash_test/ash-misc/elif2.tests8
-rw-r--r--shell/hush.c31
-rw-r--r--shell/hush_test/hush-misc/elif1.right4
-rwxr-xr-xshell/hush_test/hush-misc/elif1.tests6
-rw-r--r--shell/hush_test/hush-misc/elif2.right2
-rwxr-xr-xshell/hush_test/hush-misc/elif2.tests8
-rwxr-xr-xtestsuite/awk.tests116
-rwxr-xr-xtestsuite/hexdump.tests48
-rwxr-xr-xtestsuite/od.tests230
-rw-r--r--testsuite/testing.sh10
-rw-r--r--util-linux/hexdump.c30
-rw-r--r--util-linux/hexdump_xxd.c2
21 files changed, 892 insertions, 265 deletions
diff --git a/coreutils/od.c b/coreutils/od.c
index 6f22331e0..a7b1ba444 100644
--- a/coreutils/od.c
+++ b/coreutils/od.c
@@ -22,7 +22,9 @@
22 22
23//usage:#if !ENABLE_DESKTOP 23//usage:#if !ENABLE_DESKTOP
24//usage:#define od_trivial_usage 24//usage:#define od_trivial_usage
25//usage: "[-aBbcDdeFfHhIiLlOovXx] [FILE]" 25//usage: "[-abcdeFfhiloxsv] [FILE]"
26// We also support -BDOHXIL, but they are not documented in coreutils 9.1
27// manpage/help, so don't show them either.
26//usage:#define od_full_usage "\n\n" 28//usage:#define od_full_usage "\n\n"
27//usage: "Print FILE (or stdin) unambiguously, as octal bytes by default" 29//usage: "Print FILE (or stdin) unambiguously, as octal bytes by default"
28//usage:#endif 30//usage:#endif
@@ -144,29 +146,50 @@ odoffset(dumper_t *dumper, int argc, char ***argvp)
144 } 146 }
145} 147}
146 148
149// bb_dump_add():
150// A format string contains format units separated by [optional] whitespace.
151// A format unit contains up to three items: an iteration count, a byte count,
152// and a format.
153// The iteration count is an optional integer (default 1).
154// Each format is applied iteration count times.
155// The byte count is an optional integer. It defines the number
156// of bytes to be interpreted by each iteration of the format.
157// If an iteration count and/or a byte count is specified, a slash must be
158// placed after the iteration count and/or before the byte count
159// to disambiguate them.
160// The printf-style format is required and must be surrounded by " "s.
161// (Below, each string contains two format units)
147static const char *const add_strings[] ALIGN_PTR = { 162static const char *const add_strings[] ALIGN_PTR = {
148 "16/1 \"%3_u \" \"\\n\"", /* a */ 163 "16/1 \" %3_u\"" "\"\n\"", /* 0: a */
149 "8/2 \" %06o \" \"\\n\"", /* B, o */ 164 "8/2 \" %06o\"" "\"\n\"", /* 1: B (undocumented in od), o */
150 "16/1 \"%03o \" \"\\n\"", /* b */ 165 "16/1 \" %03o\"" "\"\n\"", /* 2: b */
151 "16/1 \"%3_c \" \"\\n\"", /* c */ 166 "16/1 \" %3_c\"" "\"\n\"", /* 3: c */
152 "8/2 \" %05u \" \"\\n\"", /* d */ 167 "8/2 \" %5u\"" "\"\n\"", /* 4: d */
153 "4/4 \" %010u \" \"\\n\"", /* D */ 168 "4/4 \" %10u\"" "\"\n\"", /* 5: D */
154 "2/8 \" %21.14e \" \"\\n\"", /* e (undocumented in od), F */ 169 "2/8 \" %24.14e\"" "\"\n\"", /* 6: e (undocumented in od), F */
155 "4/4 \" %14.7e \" \"\\n\"", /* f */ 170 "4/4 \" %15.7e\"" "\"\n\"", /* 7: f */
156 "4/4 \" %08x \" \"\\n\"", /* H, X */ 171 "4/4 \" %08x\"" "\"\n\"", /* 8: H, X */
157 "8/2 \" %04x \" \"\\n\"", /* h, x */ 172 "8/2 \" %04x\"" "\"\n\"", /* 9: h, x */
158 "4/4 \" %11d \" \"\\n\"", /* I, L, l */ 173 "4/4 \" %11d\"" "\"\n\"", /* 10: i */
159 "8/2 \" %6d \" \"\\n\"", /* i */ 174 "4/4 \" %011o\"" "\"\n\"", /* 11: O */
160 "4/4 \" %011o \" \"\\n\"", /* O */ 175 "8/2 \" %6d\"" "\"\n\"", /* 12: s */
176 /* -I,L,l: depend on word width of the arch (what is "long"?) */
177#if ULONG_MAX > 0xffffffff
178 "2/8 \" %20lld\"" "\"\n\"", /* 13: I, L, l */
179#define L_ 13
180#else
181 /* 32-bit arch: -I,L,l are the same as -i */
182#define L_ 10
183#endif
161}; 184};
162 185
163static const char od_opts[] ALIGN1 = "aBbcDdeFfHhIiLlOoXxv"; 186static const char od_opts[] ALIGN1 = "aBbcDdeFfHhIiLlOoXxsv";
164 187
165static const char od_o2si[] ALIGN1 = { 188static const char od_o2si[] ALIGN1 = {
166 0, 1, 2, 3, 5, 189 0, 1, 2, 3, 5, /* aBbcD */
167 4, 6, 6, 7, 8, 190 4, 6, 6, 7, 8, /* deFfH */
168 9, 0xa, 0xb, 0xa, 0xa, 191 9, L_, 10, L_, L_, /* hIiLl */
169 0xb, 1, 8, 9, 192 11, 1, 8, 9, 12 /* OoXxs */
170}; 193};
171 194
172int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 195int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -184,19 +207,21 @@ int od_main(int argc, char **argv)
184 if (first) { 207 if (first) {
185 first = 0; 208 first = 0;
186 bb_dump_add(dumper, "\"%07.7_Ao\n\""); 209 bb_dump_add(dumper, "\"%07.7_Ao\n\"");
187 bb_dump_add(dumper, "\"%07.7_ao \""); 210 bb_dump_add(dumper, "\"%07.7_ao\"");
188 } else { 211 } else {
189 bb_dump_add(dumper, "\" \""); 212 bb_dump_add(dumper, "\" \"");
190 } 213 }
191 bb_dump_add(dumper, add_strings[(int)od_o2si[(p - od_opts)]]); 214 bb_dump_add(dumper, add_strings[(int)od_o2si[(p - od_opts)]]);
192 } else { /* P, p, s, w, or other unhandled */ 215 } else { /* P, p, w, or other unhandled */
193 bb_show_usage(); 216 bb_show_usage();
194 } 217 }
195 } 218 }
196 if (!dumper->fshead) { 219 if (!dumper->fshead) {
197 bb_dump_add(dumper, "\"%07.7_Ao\n\""); 220 bb_dump_add(dumper, "\"%07.7_Ao\n\"");
198 bb_dump_add(dumper, "\"%07.7_ao \" 8/2 \"%06o \" \"\\n\""); 221 bb_dump_add(dumper, "\"%07.7_ao\"");
222 bb_dump_add(dumper, add_strings[1]); /* -o format is default */
199 } 223 }
224 dumper->od_eofstring = "\n";
200 225
201 argc -= optind; 226 argc -= optind;
202 argv += optind; 227 argv += optind;
@@ -205,7 +230,7 @@ int od_main(int argc, char **argv)
205 230
206 return bb_dump_dump(dumper, argv); 231 return bb_dump_dump(dumper, argv);
207} 232}
208#endif /* ENABLE_DESKTOP */ 233#endif /* !ENABLE_DESKTOP */
209 234
210/*- 235/*-
211 * Copyright (c) 1990 The Regents of the University of California. 236 * Copyright (c) 1990 The Regents of the University of California.
diff --git a/coreutils/od_bloaty.c b/coreutils/od_bloaty.c
index 1830aca83..641d93503 100644
--- a/coreutils/od_bloaty.c
+++ b/coreutils/od_bloaty.c
@@ -27,6 +27,8 @@
27//usage:#if ENABLE_DESKTOP 27//usage:#if ENABLE_DESKTOP
28//usage:#define od_trivial_usage 28//usage:#define od_trivial_usage
29//usage: "[-abcdfhilovxs] [-t TYPE] [-A RADIX] [-N SIZE] [-j SKIP] [-S MINSTR] [-w WIDTH] [FILE]..." 29//usage: "[-abcdfhilovxs] [-t TYPE] [-A RADIX] [-N SIZE] [-j SKIP] [-S MINSTR] [-w WIDTH] [FILE]..."
30// We also support -BDOHXIL, but they are not documented in coreutils 9.1
31// manpage/help, so don't show them either.
30// We don't support: 32// We don't support:
31// ... [FILE] [[+]OFFSET[.][b]] 33// ... [FILE] [[+]OFFSET[.][b]]
32// Support is buggy for: 34// Support is buggy for:
@@ -43,26 +45,33 @@ enum {
43 OPT_b = 1 << 3, 45 OPT_b = 1 << 3,
44 OPT_c = 1 << 4, 46 OPT_c = 1 << 4,
45 OPT_d = 1 << 5, 47 OPT_d = 1 << 5,
46 OPT_f = 1 << 6, 48 OPT_D = 1 << 6, /* undocumented in coreutils 9.1 */
47 OPT_h = 1 << 7, 49 OPT_f = 1 << 7,
48 OPT_i = 1 << 8, 50 OPT_h = 1 << 8,
49 OPT_j = 1 << 9, 51 OPT_H = 1 << 9, /* undocumented in coreutils 9.1 */
50 OPT_l = 1 << 10, 52 OPT_i = 1 << 10,
51 OPT_o = 1 << 11, 53 OPT_I = 1 << 11, /* undocumented in coreutils 9.1 */
52 OPT_t = 1 << 12, 54 OPT_j = 1 << 12,
55 OPT_l = 1 << 13,
56 OPT_L = 1 << 14, /* undocumented in coreutils 9.1 */
57 OPT_o = 1 << 15,
58 OPT_O = 1 << 16, /* undocumented in coreutils 9.1 */
59 OPT_B = 1 << 17, /* undocumented synonym to -o */
60 OPT_t = 1 << 18,
53 /* When zero and two or more consecutive blocks are equal, format 61 /* When zero and two or more consecutive blocks are equal, format
54 only the first block and output an asterisk alone on the following 62 only the first block and output an asterisk alone on the following
55 line to indicate that identical blocks have been elided: */ 63 line to indicate that identical blocks have been elided: */
56 OPT_v = 1 << 13, 64 OPT_v = 1 << 19,
57 OPT_x = 1 << 14, 65 OPT_x = 1 << 20,
58 OPT_s = 1 << 15, 66 OPT_X = 1 << 21, /* undocumented in coreutils 9.1 */
59 OPT_S = 1 << 16, 67 OPT_s = 1 << 22,
60 OPT_w = 1 << 17, 68 OPT_S = 1 << 23,
61 OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS, 69 OPT_w = 1 << 24,
70 OPT_traditional = (1 << 25) * ENABLE_LONG_OPTS,
62}; 71};
63 72
64#define OD_GETOPT32() getopt32long(argv, \ 73#define OD_GETOPT32() getopt32long(argv, \
65 "A:N:abcdfhij:lot:*vxsS:w:+:", od_longopts, \ 74 "A:N:abcdDfhHiIj:lLoOBt:*vxXsS:w:+:", od_longopts, \
66 /* -w with optional param */ \ 75 /* -w with optional param */ \
67 /* -S was -s and also had optional parameter */ \ 76 /* -S was -s and also had optional parameter */ \
68 /* but in coreutils 6.3 it was renamed and now has */ \ 77 /* but in coreutils 6.3 it was renamed and now has */ \
@@ -1246,20 +1255,29 @@ int od_main(int argc UNUSED_PARAM, char **argv)
1246 if (opt & OPT_N) { 1255 if (opt & OPT_N) {
1247 max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm_suffixes); 1256 max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm_suffixes);
1248 } 1257 }
1258
1249 if (opt & OPT_a) decode_format_string("a"); 1259 if (opt & OPT_a) decode_format_string("a");
1250 if (opt & OPT_b) decode_format_string("oC"); 1260 if (opt & OPT_b) decode_format_string("oC");
1251 if (opt & OPT_c) decode_format_string("c"); 1261 if (opt & OPT_c) decode_format_string("c");
1252 if (opt & OPT_d) decode_format_string("u2"); 1262 if (opt & OPT_d) decode_format_string("u2");
1263 if (opt & OPT_D) decode_format_string("uI");
1253 if (opt & OPT_f) decode_format_string("fF"); 1264 if (opt & OPT_f) decode_format_string("fF");
1254 if (opt & OPT_h) decode_format_string("x2"); 1265 if (opt & (OPT_h|OPT_x)) decode_format_string("x2");
1255 if (opt & OPT_i) decode_format_string("d2"); 1266 if (opt & (OPT_H|OPT_X)) decode_format_string("xI");
1267 /* -I,L,l: depend on word width of the arch (what is "long"?) */
1268#if ULONG_MAX > 0xffffffff
1269 if (opt & OPT_i) decode_format_string("dI");
1270 if (opt & (OPT_I|OPT_l|OPT_L)) decode_format_string("dL");
1271#else
1272 /* 32-bit arch: -I,L,l are the same as -i */
1273 if (opt & (OPT_i|OPT_I|OPT_l|OPT_L)) decode_format_string("dI");
1274#endif
1256 if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm_suffixes); 1275 if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm_suffixes);
1257 if (opt & OPT_l) decode_format_string("d4"); 1276 if (opt & (OPT_o|OPT_B)) decode_format_string("o2");
1258 if (opt & OPT_o) decode_format_string("o2"); 1277 if (opt & OPT_O) decode_format_string("oI");
1259 while (lst_t) { 1278 while (lst_t) {
1260 decode_format_string(llist_pop(&lst_t)); 1279 decode_format_string(llist_pop(&lst_t));
1261 } 1280 }
1262 if (opt & OPT_x) decode_format_string("x2");
1263 if (opt & OPT_s) decode_format_string("d2"); 1281 if (opt & OPT_s) decode_format_string("d2");
1264 if (opt & OPT_S) { 1282 if (opt & OPT_S) {
1265 G.string_min = xstrtou_sfx(str_S, 0, bkm_suffixes); 1283 G.string_min = xstrtou_sfx(str_S, 0, bkm_suffixes);
diff --git a/editors/awk.c b/editors/awk.c
index 2c1272554..878fffa1a 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)
@@ -546,7 +548,6 @@ struct globals {
546 chain beginseq, mainseq, endseq; 548 chain beginseq, mainseq, endseq;
547 chain *seq; 549 chain *seq;
548 node *break_ptr, *continue_ptr; 550 node *break_ptr, *continue_ptr;
549 rstream *iF;
550 xhash *ahash; /* argument names, used only while parsing function bodies */ 551 xhash *ahash; /* argument names, used only while parsing function bodies */
551 xhash *fnhash; /* function names, used only in parsing stage */ 552 xhash *fnhash; /* function names, used only in parsing stage */
552 xhash *vhash; /* variables and arrays */ 553 xhash *vhash; /* variables and arrays */
@@ -555,7 +556,7 @@ struct globals {
555 const char *g_progname; 556 const char *g_progname;
556 int g_lineno; 557 int g_lineno;
557 int nfields; 558 int nfields;
558 int maxfields; /* used in fsrealloc() only */ 559 unsigned maxfields;
559 var *Fields; 560 var *Fields;
560 char *g_pos; 561 char *g_pos;
561 char g_saved_ch; 562 char g_saved_ch;
@@ -579,11 +580,13 @@ struct globals2 {
579 580
580 var *intvar[NUM_INTERNAL_VARS]; /* often used */ 581 var *intvar[NUM_INTERNAL_VARS]; /* often used */
581 582
583 rstream iF;
584
582 /* former statics from various functions */ 585 /* former statics from various functions */
583 char *split_f0__fstrings; 586 char *split_f0__fstrings;
584 587
585 rstream next_input_file__rsm; 588 unsigned next_input_file__argind;
586 smallint next_input_file__files_happen; 589 smallint next_input_file__input_file_seen;
587 590
588 smalluint exitcode; 591 smalluint exitcode;
589 592
@@ -618,7 +621,6 @@ struct globals2 {
618#define seq (G1.seq ) 621#define seq (G1.seq )
619#define break_ptr (G1.break_ptr ) 622#define break_ptr (G1.break_ptr )
620#define continue_ptr (G1.continue_ptr) 623#define continue_ptr (G1.continue_ptr)
621#define iF (G1.iF )
622#define ahash (G1.ahash ) 624#define ahash (G1.ahash )
623#define fnhash (G1.fnhash ) 625#define fnhash (G1.fnhash )
624#define vhash (G1.vhash ) 626#define vhash (G1.vhash )
@@ -644,6 +646,7 @@ struct globals2 {
644#define t_string (G.t_string ) 646#define t_string (G.t_string )
645#define t_lineno (G.t_lineno ) 647#define t_lineno (G.t_lineno )
646#define intvar (G.intvar ) 648#define intvar (G.intvar )
649#define iF (G.iF )
647#define fsplitter (G.fsplitter ) 650#define fsplitter (G.fsplitter )
648#define rsplitter (G.rsplitter ) 651#define rsplitter (G.rsplitter )
649#define g_buf (G.g_buf ) 652#define g_buf (G.g_buf )
@@ -978,6 +981,11 @@ static var *setvar_s(var *v, const char *value)
978 return setvar_p(v, (value && *value) ? xstrdup(value) : NULL); 981 return setvar_p(v, (value && *value) ? xstrdup(value) : NULL);
979} 982}
980 983
984static var *setvar_sn(var *v, const char *value, int len)
985{
986 return setvar_p(v, (value && *value && len > 0) ? xstrndup(value, len) : NULL);
987}
988
981/* same as setvar_s but sets USER flag */ 989/* same as setvar_s but sets USER flag */
982static var *setvar_u(var *v, const char *value) 990static var *setvar_u(var *v, const char *value)
983{ 991{
@@ -1005,6 +1013,11 @@ static var *setvar_i(var *v, double value)
1005 return v; 1013 return v;
1006} 1014}
1007 1015
1016static void setvar_ERRNO(void)
1017{
1018 setvar_i(intvar[ERRNO], errno);
1019}
1020
1008static const char *getvar_s(var *v) 1021static const char *getvar_s(var *v)
1009{ 1022{
1010 /* if v is numeric and has no cached string, convert it to string */ 1023 /* if v is numeric and has no cached string, convert it to string */
@@ -1290,7 +1303,7 @@ static uint32_t next_token(uint32_t expected)
1290 save_tclass = tc; 1303 save_tclass = tc;
1291 save_info = t_info; 1304 save_info = t_info;
1292 tc = TC_BINOPX; 1305 tc = TC_BINOPX;
1293 t_info = OC_CONCAT | SS | P(35); 1306 t_info = OC_CONCAT | SS | PRECEDENCE(35);
1294 } 1307 }
1295 1308
1296 t_tclass = tc; 1309 t_tclass = tc;
@@ -1350,9 +1363,8 @@ static node *parse_expr(uint32_t term_tc)
1350{ 1363{
1351 node sn; 1364 node sn;
1352 node *cn = &sn; 1365 node *cn = &sn;
1353 node *vn, *glptr; 1366 node *glptr;
1354 uint32_t tc, expected_tc; 1367 uint32_t tc, expected_tc;
1355 var *v;
1356 1368
1357 debug_printf_parse("%s() term_tc(%x):", __func__, term_tc); 1369 debug_printf_parse("%s() term_tc(%x):", __func__, term_tc);
1358 debug_parse_print_tc(term_tc); 1370 debug_parse_print_tc(term_tc);
@@ -1363,11 +1375,12 @@ static node *parse_expr(uint32_t term_tc)
1363 expected_tc = TS_OPERAND | TS_UOPPRE | TC_REGEXP | term_tc; 1375 expected_tc = TS_OPERAND | TS_UOPPRE | TC_REGEXP | term_tc;
1364 1376
1365 while (!((tc = next_token(expected_tc)) & term_tc)) { 1377 while (!((tc = next_token(expected_tc)) & term_tc)) {
1378 node *vn;
1366 1379
1367 if (glptr && (t_info == TI_LESS)) { 1380 if (glptr && (t_info == TI_LESS)) {
1368 /* input redirection (<) attached to glptr node */ 1381 /* input redirection (<) attached to glptr node */
1369 debug_printf_parse("%s: input redir\n", __func__); 1382 debug_printf_parse("%s: input redir\n", __func__);
1370 cn = glptr->l.n = new_node(OC_CONCAT | SS | P(37)); 1383 cn = glptr->l.n = new_node(OC_CONCAT | SS | PRECEDENCE(37));
1371 cn->a.n = glptr; 1384 cn->a.n = glptr;
1372 expected_tc = TS_OPERAND | TS_UOPPRE; 1385 expected_tc = TS_OPERAND | TS_UOPPRE;
1373 glptr = NULL; 1386 glptr = NULL;
@@ -1379,24 +1392,42 @@ static node *parse_expr(uint32_t term_tc)
1379 * previous operators with higher priority */ 1392 * previous operators with higher priority */
1380 vn = cn; 1393 vn = cn;
1381 while (((t_info & PRIMASK) > (vn->a.n->info & PRIMASK2)) 1394 while (((t_info & PRIMASK) > (vn->a.n->info & PRIMASK2))
1382 || ((t_info == vn->info) && t_info == TI_COLON) 1395 || (t_info == vn->info && t_info == TI_COLON)
1383 ) { 1396 ) {
1384 vn = vn->a.n; 1397 vn = vn->a.n;
1385 if (!vn->a.n) syntax_error(EMSG_UNEXP_TOKEN); 1398 if (!vn->a.n) syntax_error(EMSG_UNEXP_TOKEN);
1386 } 1399 }
1387 if (t_info == TI_TERNARY) 1400 if (t_info == TI_TERNARY)
1388//TODO: why? 1401//TODO: why?
1389 t_info += P(6); 1402 t_info += PRECEDENCE(6);
1390 cn = vn->a.n->r.n = new_node(t_info); 1403 cn = vn->a.n->r.n = new_node(t_info);
1391 cn->a.n = vn->a.n; 1404 cn->a.n = vn->a.n;
1392 if (tc & TS_BINOP) { 1405 if (tc & TS_BINOP) {
1393 cn->l.n = vn; 1406 cn->l.n = vn;
1394//FIXME: this is the place to detect and reject assignments to non-lvalues. 1407
1395//Currently we allow "assignments" to consts and temporaries, nonsense like this: 1408 /* Prevent:
1396// awk 'BEGIN { "qwe" = 1 }' 1409 * awk 'BEGIN { "qwe" = 1 }'
1397// awk 'BEGIN { 7 *= 7 }' 1410 * awk 'BEGIN { 7 *= 7 }'
1398// awk 'BEGIN { length("qwe") = 1 }' 1411 * awk 'BEGIN { length("qwe") = 1 }'
1399// 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
1400 expected_tc = TS_OPERAND | TS_UOPPRE | TC_REGEXP; 1431 expected_tc = TS_OPERAND | TS_UOPPRE | TC_REGEXP;
1401 if (t_info == TI_PGETLINE) { 1432 if (t_info == TI_PGETLINE) {
1402 /* it's a pipe */ 1433 /* it's a pipe */
@@ -1432,6 +1463,8 @@ static node *parse_expr(uint32_t term_tc)
1432 /* one should be very careful with switch on tclass - 1463 /* one should be very careful with switch on tclass -
1433 * only simple tclasses should be used (TC_xyz, not TS_xyz) */ 1464 * only simple tclasses should be used (TC_xyz, not TS_xyz) */
1434 switch (tc) { 1465 switch (tc) {
1466 var *v;
1467
1435 case TC_VARIABLE: 1468 case TC_VARIABLE:
1436 case TC_ARRAY: 1469 case TC_ARRAY:
1437 debug_printf_parse("%s: TC_VARIABLE | TC_ARRAY\n", __func__); 1470 debug_printf_parse("%s: TC_VARIABLE | TC_ARRAY\n", __func__);
@@ -1452,14 +1485,14 @@ static node *parse_expr(uint32_t term_tc)
1452 case TC_NUMBER: 1485 case TC_NUMBER:
1453 case TC_STRING: 1486 case TC_STRING:
1454 debug_printf_parse("%s: TC_NUMBER | TC_STRING\n", __func__); 1487 debug_printf_parse("%s: TC_NUMBER | TC_STRING\n", __func__);
1455 cn->info = OC_VAR; 1488 cn->info = OC_CONST;
1456 v = cn->l.v = xzalloc(sizeof(var)); 1489 v = cn->l.v = xzalloc(sizeof(var));
1457 if (tc & TC_NUMBER) 1490 if (tc & TC_NUMBER) {
1458 setvar_i(v, t_double); 1491 setvar_i(v, t_double);
1459 else { 1492 } else {
1460 setvar_s(v, t_string); 1493 setvar_s(v, t_string);
1461 expected_tc &= ~TC_UOPPOST; /* "str"++ is not allowed */
1462 } 1494 }
1495 expected_tc &= ~TC_UOPPOST; /* NUM++, "str"++ not allowed */
1463 break; 1496 break;
1464 1497
1465 case TC_REGEXP: 1498 case TC_REGEXP:
@@ -1931,9 +1964,9 @@ static void fsrealloc(int size)
1931{ 1964{
1932 int i, newsize; 1965 int i, newsize;
1933 1966
1934 if (size >= maxfields) { 1967 if ((unsigned)size >= maxfields) {
1935 /* Sanity cap, easier than catering for overflows */ 1968 /* Sanity cap, easier than catering for over/underflows */
1936 if (size > 0xffffff) 1969 if ((unsigned)size > 0xffffff)
1937 bb_die_memory_exhausted(); 1970 bb_die_memory_exhausted();
1938 1971
1939 i = maxfields; 1972 i = maxfields;
@@ -2049,13 +2082,17 @@ static int awk_split(const char *s, node *spl, char **slist)
2049 } 2082 }
2050 return n; 2083 return n;
2051 } 2084 }
2052 /* space split */ 2085 /* space split: "In the special case that FS is a single space,
2086 * fields are separated by runs of spaces and/or tabs and/or newlines"
2087 */
2053 while (*s) { 2088 while (*s) {
2054 s = skip_whitespace(s); 2089 /* s = skip_whitespace(s); -- WRONG (also skips \v \f \r) */
2090 while (*s == ' ' || *s == '\t' || *s == '\n')
2091 s++;
2055 if (!*s) 2092 if (!*s)
2056 break; 2093 break;
2057 n++; 2094 n++;
2058 while (*s && !isspace(*s)) 2095 while (*s && !(*s == ' ' || *s == '\t' || *s == '\n'))
2059 *s1++ = *s++; 2096 *s1++ = *s++;
2060 *s1++ = '\0'; 2097 *s1++ = '\0';
2061 } 2098 }
@@ -2232,9 +2269,9 @@ static int awk_getline(rstream *rsm, var *v)
2232{ 2269{
2233 char *b; 2270 char *b;
2234 regmatch_t pmatch[1]; 2271 regmatch_t pmatch[1];
2235 int size, a, p, pp = 0; 2272 int p, pp;
2236 int fd, so, eo, r, rp; 2273 int fd, so, eo, retval, rp;
2237 char c, *m, *s; 2274 char *m, *s;
2238 2275
2239 debug_printf_eval("entered %s()\n", __func__); 2276 debug_printf_eval("entered %s()\n", __func__);
2240 2277
@@ -2243,23 +2280,22 @@ static int awk_getline(rstream *rsm, var *v)
2243 */ 2280 */
2244 fd = fileno(rsm->F); 2281 fd = fileno(rsm->F);
2245 m = rsm->buffer; 2282 m = rsm->buffer;
2246 a = rsm->adv; 2283 if (!m)
2284 m = qrealloc(m, 256, &rsm->size);
2247 p = rsm->pos; 2285 p = rsm->pos;
2248 size = rsm->size;
2249 c = (char) rsplitter.n.info;
2250 rp = 0; 2286 rp = 0;
2251 2287 pp = 0;
2252 if (!m)
2253 m = qrealloc(m, 256, &size);
2254 2288
2255 do { 2289 do {
2256 b = m + a; 2290 b = m + rsm->adv;
2257 so = eo = p; 2291 so = eo = p;
2258 r = 1; 2292 retval = 1;
2259 if (p > 0) { 2293 if (p > 0) {
2294 char c = (char) rsplitter.n.info;
2260 if (rsplitter.n.info == TI_REGEXP) { 2295 if (rsplitter.n.info == TI_REGEXP) {
2261 if (regexec(icase ? rsplitter.n.r.ire : rsplitter.n.l.re, 2296 if (regexec(icase ? rsplitter.n.r.ire : rsplitter.n.l.re,
2262 b, 1, pmatch, 0) == 0) { 2297 b, 1, pmatch, 0) == 0
2298 ) {
2263 so = pmatch[0].rm_so; 2299 so = pmatch[0].rm_so;
2264 eo = pmatch[0].rm_eo; 2300 eo = pmatch[0].rm_eo;
2265 if (b[eo] != '\0') 2301 if (b[eo] != '\0')
@@ -2288,45 +2324,38 @@ static int awk_getline(rstream *rsm, var *v)
2288 } 2324 }
2289 } 2325 }
2290 2326
2291 if (a > 0) { 2327 if (rsm->adv > 0) {
2292 memmove(m, m+a, p+1); 2328 memmove(m, m+rsm->adv, p+1);
2293 b = m; 2329 b = m;
2294 a = 0; 2330 rsm->adv = 0;
2295 } 2331 }
2296 2332
2297 m = qrealloc(m, a+p+128, &size); 2333 b = m = qrealloc(m, p+128, &rsm->size);
2298 b = m + a;
2299 pp = p; 2334 pp = p;
2300 p += safe_read(fd, b+p, size-p-1); 2335 p += safe_read(fd, b+p, rsm->size - p - 1);
2301 if (p < pp) { 2336 if (p < pp) {
2302 p = 0; 2337 p = 0;
2303 r = 0; 2338 retval = 0;
2304 setvar_i(intvar[ERRNO], errno); 2339 setvar_ERRNO();
2305 } 2340 }
2306 b[p] = '\0'; 2341 b[p] = '\0';
2307
2308 } while (p > pp); 2342 } while (p > pp);
2309 2343
2310 if (p == 0) { 2344 if (p == 0) {
2311 r--; 2345 retval--;
2312 } else { 2346 } else {
2313 c = b[so]; b[so] = '\0'; 2347 setvar_sn(v, b+rp, so-rp);
2314 setvar_s(v, b+rp);
2315 v->type |= VF_USER; 2348 v->type |= VF_USER;
2316 b[so] = c; 2349 setvar_sn(intvar[RT], b+so, eo-so);
2317 c = b[eo]; b[eo] = '\0';
2318 setvar_s(intvar[RT], b+so);
2319 b[eo] = c;
2320 } 2350 }
2321 2351
2322 rsm->buffer = m; 2352 rsm->buffer = m;
2323 rsm->adv = a + eo; 2353 rsm->adv += eo;
2324 rsm->pos = p - eo; 2354 rsm->pos = p - eo;
2325 rsm->size = size;
2326 2355
2327 debug_printf_eval("returning from %s(): %d\n", __func__, r); 2356 debug_printf_eval("returning from %s(): %d\n", __func__, retval);
2328 2357
2329 return r; 2358 return retval;
2330} 2359}
2331 2360
2332/* formatted output into an allocated buffer, return ptr to buffer */ 2361/* formatted output into an allocated buffer, return ptr to buffer */
@@ -2382,7 +2411,7 @@ static char *awk_printf(node *n, size_t *len)
2382 while (1) { 2411 while (1) {
2383 if (isalpha(c)) 2412 if (isalpha(c))
2384 break; 2413 break;
2385 if (c == '*') 2414 if (c == '*') /* gawk supports %*d and %*.*f, we don't... */
2386 syntax_error("%*x formats are not supported"); 2415 syntax_error("%*x formats are not supported");
2387 c = *++f; 2416 c = *++f;
2388 if (!c) { /* "....%...." and no letter found after % */ 2417 if (!c) { /* "....%...." and no letter found after % */
@@ -2415,12 +2444,18 @@ static char *awk_printf(node *n, size_t *len)
2415 double d = getvar_i(arg); 2444 double d = getvar_i(arg);
2416 if (strchr("diouxX", c)) { 2445 if (strchr("diouxX", c)) {
2417//TODO: make it wider here (%x -> %llx etc)? 2446//TODO: make it wider here (%x -> %llx etc)?
2447//Can even print the value into a temp string with %.0f,
2448//then replace diouxX with s and print that string.
2449//This will correctly print even very large numbers,
2450//but some replacements are not equivalent:
2451//%09d -> %09s: breaks zero-padding;
2452//%+d -> %+s: won't prepend +; etc
2418 s = xasprintf(s, (int)d); 2453 s = xasprintf(s, (int)d);
2419 } else if (strchr("eEfFgGaA", c)) { 2454 } else if (strchr("eEfFgGaA", c)) {
2420 s = xasprintf(s, d); 2455 s = xasprintf(s, d);
2421 } else { 2456 } else {
2422//TODO: GNU Awk 5.0.1: printf "%W" prints "%W", does not error out 2457 /* gawk 5.1.1 printf("%W") prints "%W", does not error out */
2423 syntax_error(EMSG_INV_FMT); 2458 s = xstrndup(s, f - s);
2424 } 2459 }
2425 } 2460 }
2426 slen = strlen(s); 2461 slen = strlen(s);
@@ -2457,9 +2492,9 @@ static char *awk_printf(node *n, size_t *len)
2457 * store result into (dest), return number of substitutions. 2492 * store result into (dest), return number of substitutions.
2458 * If nm = 0, replace all matches. 2493 * If nm = 0, replace all matches.
2459 * If src or dst is NULL, use $0. 2494 * If src or dst is NULL, use $0.
2460 * If subexp != 0, enable subexpression matching (\1-\9). 2495 * If subexp != 0, enable subexpression matching (\0-\9).
2461 */ 2496 */
2462static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest, int subexp) 2497static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest /*,int subexp*/)
2463{ 2498{
2464 char *resbuf; 2499 char *resbuf;
2465 const char *sp; 2500 const char *sp;
@@ -2467,17 +2502,48 @@ static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest, int
2467 int regexec_flags; 2502 int regexec_flags;
2468 regmatch_t pmatch[10]; 2503 regmatch_t pmatch[10];
2469 regex_t sreg, *regex; 2504 regex_t sreg, *regex;
2470 2505 /* True only if called to implement gensub(): */
2506 int subexp = (src != dest);
2507#if defined(REG_STARTEND)
2508 const char *src_string;
2509 size_t src_strlen;
2510 regexec_flags = REG_STARTEND;
2511#else
2512 regexec_flags = 0;
2513#endif
2471 resbuf = NULL; 2514 resbuf = NULL;
2472 residx = 0; 2515 residx = 0;
2473 match_no = 0; 2516 match_no = 0;
2474 regexec_flags = 0;
2475 regex = as_regex(rn, &sreg); 2517 regex = as_regex(rn, &sreg);
2476 sp = getvar_s(src ? src : intvar[F0]); 2518 sp = getvar_s(src ? src : intvar[F0]);
2519#if defined(REG_STARTEND)
2520 src_string = sp;
2521 src_strlen = strlen(src_string);
2522#endif
2477 replen = strlen(repl); 2523 replen = strlen(repl);
2478 while (regexec(regex, sp, 10, pmatch, regexec_flags) == 0) { 2524 for (;;) {
2479 int so = pmatch[0].rm_so; 2525 int so, eo;
2480 int eo = pmatch[0].rm_eo; 2526
2527#if defined(REG_STARTEND)
2528// REG_STARTEND: "This flag is a BSD extension, not present in POSIX"
2529 size_t start_ofs = sp - src_string;
2530 pmatch[0].rm_so = start_ofs;
2531 pmatch[0].rm_eo = src_strlen;
2532 if (regexec(regex, src_string, 10, pmatch, regexec_flags) != 0)
2533 break;
2534 eo = pmatch[0].rm_eo - start_ofs;
2535 so = pmatch[0].rm_so - start_ofs;
2536#else
2537// BUG:
2538// gsub(/\<b*/,"") on "abc" matches empty string at "a...",
2539// advances sp one char (see "Empty match" comment later) to "bc"
2540// ... and erroneously matches "b" even though it is NOT at the word start.
2541 enum { start_ofs = 0 };
2542 if (regexec(regex, sp, 10, pmatch, regexec_flags) != 0)
2543 break;
2544 so = pmatch[0].rm_so;
2545 eo = pmatch[0].rm_eo;
2546#endif
2481 2547
2482 //bb_error_msg("match %u: [%u,%u] '%s'%p", match_no+1, so, eo, sp,sp); 2548 //bb_error_msg("match %u: [%u,%u] '%s'%p", match_no+1, so, eo, sp,sp);
2483 resbuf = qrealloc(resbuf, residx + eo + replen, &resbufsize); 2549 resbuf = qrealloc(resbuf, residx + eo + replen, &resbufsize);
@@ -2485,51 +2551,41 @@ static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest, int
2485 residx += eo; 2551 residx += eo;
2486 if (++match_no >= nm) { 2552 if (++match_no >= nm) {
2487 const char *s; 2553 const char *s;
2488 int nbs; 2554 int bslash;
2489 2555
2490 /* replace */ 2556 /* replace */
2491 residx -= (eo - so); 2557 residx -= (eo - so);
2492 nbs = 0; 2558 bslash = 0;
2493 for (s = repl; *s; s++) { 2559 for (s = repl; *s; s++) {
2494 char c = resbuf[residx++] = *s; 2560 char c = *s;
2495 if (c == '\\') { 2561 if (c == '\\' && s[1]) {
2496 nbs++; 2562 bslash ^= 1;
2497 continue; 2563 if (bslash)
2564 continue;
2498 } 2565 }
2499 if (c == '&' || (subexp && c >= '0' && c <= '9')) { 2566 if ((!bslash && c == '&')
2500 int j; 2567 || (subexp && bslash && c >= '0' && c <= '9')
2501 residx -= ((nbs + 3) >> 1); 2568 ) {
2502 j = 0; 2569 int n, j = 0;
2503 if (c != '&') { 2570 if (c != '&') {
2504 j = c - '0'; 2571 j = c - '0';
2505 nbs++;
2506 } 2572 }
2507 if (nbs % 2) { 2573 n = pmatch[j].rm_eo - pmatch[j].rm_so;
2508 resbuf[residx++] = c; 2574 resbuf = qrealloc(resbuf, residx + replen + n, &resbufsize);
2509 } else { 2575 memcpy(resbuf + residx, sp + pmatch[j].rm_so - start_ofs, n);
2510 int n = pmatch[j].rm_eo - pmatch[j].rm_so; 2576 residx += n;
2511 resbuf = qrealloc(resbuf, residx + replen + n, &resbufsize); 2577 } else
2512 memcpy(resbuf + residx, sp + pmatch[j].rm_so, n); 2578 resbuf[residx++] = c;
2513 residx += n; 2579 bslash = 0;
2514 }
2515 }
2516 nbs = 0;
2517 } 2580 }
2518 } 2581 }
2519 2582
2520 regexec_flags = REG_NOTBOL;
2521 sp += eo; 2583 sp += eo;
2522 if (match_no == nm) 2584 if (match_no == nm)
2523 break; 2585 break;
2524 if (eo == so) { 2586 if (eo == so) {
2525 /* Empty match (e.g. "b*" will match anywhere). 2587 /* Empty match (e.g. "b*" will match anywhere).
2526 * Advance by one char. */ 2588 * Advance by one char. */
2527//BUG (bug 1333):
2528//gsub(/\<b*/,"") on "abc" will reach this point, advance to "bc"
2529//... and will erroneously match "b" even though it is NOT at the word start.
2530//we need REG_NOTBOW but it does not exist...
2531//TODO: if EXTRA_COMPAT=y, use GNU matching and re_search,
2532//it should be able to do it correctly.
2533 /* Subtle: this is safe only because 2589 /* Subtle: this is safe only because
2534 * qrealloc allocated at least one extra byte */ 2590 * qrealloc allocated at least one extra byte */
2535 resbuf[residx] = *sp; 2591 resbuf[residx] = *sp;
@@ -2538,6 +2594,7 @@ static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest, int
2538 sp++; 2594 sp++;
2539 residx++; 2595 residx++;
2540 } 2596 }
2597 regexec_flags |= REG_NOTBOL;
2541 } 2598 }
2542 2599
2543 resbuf = qrealloc(resbuf, residx + strlen(sp), &resbufsize); 2600 resbuf = qrealloc(resbuf, residx + strlen(sp), &resbufsize);
@@ -2669,8 +2726,6 @@ static NOINLINE var *exec_builtin(node *op, var *res)
2669 } 2726 }
2670 2727
2671 case B_ss: { 2728 case B_ss: {
2672 char *s;
2673
2674 l = strlen(as[0]); 2729 l = strlen(as[0]);
2675 i = getvar_i(av[1]) - 1; 2730 i = getvar_i(av[1]) - 1;
2676 if (i > l) 2731 if (i > l)
@@ -2680,8 +2735,7 @@ static NOINLINE var *exec_builtin(node *op, var *res)
2680 n = (nargs > 2) ? getvar_i(av[2]) : l-i; 2735 n = (nargs > 2) ? getvar_i(av[2]) : l-i;
2681 if (n < 0) 2736 if (n < 0)
2682 n = 0; 2737 n = 0;
2683 s = xstrndup(as[0]+i, n); 2738 setvar_sn(res, as[0]+i, n);
2684 setvar_p(res, s);
2685 break; 2739 break;
2686 } 2740 }
2687 2741
@@ -2758,8 +2812,7 @@ static NOINLINE var *exec_builtin(node *op, var *res)
2758 i = strftime(g_buf, MAXVARFMT, 2812 i = strftime(g_buf, MAXVARFMT,
2759 ((nargs > 0) ? as[0] : "%a %b %d %H:%M:%S %Z %Y"), 2813 ((nargs > 0) ? as[0] : "%a %b %d %H:%M:%S %Z %Y"),
2760 localtime(&tt)); 2814 localtime(&tt));
2761 g_buf[i] = '\0'; 2815 setvar_sn(res, g_buf, i);
2762 setvar_s(res, g_buf);
2763 break; 2816 break;
2764 2817
2765 case B_mt: 2818 case B_mt:
@@ -2770,16 +2823,16 @@ static NOINLINE var *exec_builtin(node *op, var *res)
2770 res = do_match(an[1], as[0]); 2823 res = do_match(an[1], as[0]);
2771 break; 2824 break;
2772 2825
2773 case B_ge: 2826 case B_ge: /* gensub(regex, repl, matchnum, string) */
2774 awk_sub(an[0], as[1], getvar_i(av[2]), av[3], res, TRUE); 2827 awk_sub(an[0], as[1], /*matchnum:*/getvar_i(av[2]), /*src:*/av[3], /*dst:*/res/*, TRUE*/);
2775 break; 2828 break;
2776 2829
2777 case B_gs: 2830 case B_gs: /* gsub(regex, repl, string) */
2778 setvar_i(res, awk_sub(an[0], as[1], 0, av[2], av[2], FALSE)); 2831 setvar_i(res, awk_sub(an[0], as[1], /*matchnum:all*/0, /*src:*/av[2], /*dst:*/av[2]/*, FALSE*/));
2779 break; 2832 break;
2780 2833
2781 case B_su: 2834 case B_su: /* sub(regex, repl, string) */
2782 setvar_i(res, awk_sub(an[0], as[1], 1, av[2], av[2], FALSE)); 2835 setvar_i(res, awk_sub(an[0], as[1], /*matchnum:first*/1, /*src:*/av[2], /*dst:*/av[2]/*, FALSE*/));
2783 break; 2836 break;
2784 } 2837 }
2785 2838
@@ -2796,7 +2849,7 @@ static NOINLINE var *exec_builtin(node *op, var *res)
2796 2849
2797/* if expr looks like "var=value", perform assignment and return 1, 2850/* if expr looks like "var=value", perform assignment and return 1,
2798 * otherwise return 0 */ 2851 * otherwise return 0 */
2799static int is_assignment(const char *expr) 2852static int try_to_assign(const char *expr)
2800{ 2853{
2801 char *exprc, *val; 2854 char *exprc, *val;
2802 2855
@@ -2825,42 +2878,55 @@ static void set_text_mode(FILE *f)
2825#endif 2878#endif
2826 2879
2827/* switch to next input file */ 2880/* switch to next input file */
2828static rstream *next_input_file(void) 2881static int next_input_file(void)
2829{ 2882{
2830#define rsm (G.next_input_file__rsm) 2883#define input_file_seen (G.next_input_file__input_file_seen)
2831#define files_happen (G.next_input_file__files_happen) 2884#define argind (G.next_input_file__argind)
2885 const char *fname;
2832 2886
2833 const char *fname, *ind; 2887 if (iF.F) {
2834 2888 fclose(iF.F);
2835 if (rsm.F) 2889 iF.F = NULL;
2836 fclose(rsm.F); 2890 iF.pos = iF.adv = 0;
2837 rsm.F = NULL; 2891 }
2838 rsm.pos = rsm.adv = 0;
2839 2892
2840 for (;;) { 2893 for (;;) {
2841 if (getvar_i(intvar[ARGIND])+1 >= getvar_i(intvar[ARGC])) { 2894 /* GNU Awk 5.1.1 does not _read_ ARGIND (but does read ARGC).
2842 if (files_happen) 2895 * It only sets ARGIND to 1, 2, 3... for every command-line filename
2843 return NULL; 2896 * (VAR=VAL params cause a gap in numbering).
2897 * If there are none and stdin is used, then ARGIND is not modified:
2898 * if it is set by e.g. 'BEGIN { ARGIND="foo" }', that value will
2899 * still be there.
2900 */
2901 argind++;
2902 if (argind >= getvar_i(intvar[ARGC])) {
2903 if (input_file_seen)
2904 return FALSE;
2844 fname = "-"; 2905 fname = "-";
2845 rsm.F = stdin; 2906 iF.F = stdin;
2846 break; 2907 break;
2847 } 2908 }
2848 ind = getvar_s(incvar(intvar[ARGIND])); 2909 fname = getvar_s(findvar(iamarray(intvar[ARGV]), utoa(argind)));
2849 fname = getvar_s(findvar(iamarray(intvar[ARGV]), ind)); 2910 if (fname && *fname) {
2850 if (fname && *fname && !is_assignment(fname)) { 2911 /* "If a filename on the command line has the form
2851 rsm.F = xfopen_stdin(fname); 2912 * var=val it is treated as a variable assignment"
2913 */
2914 if (try_to_assign(fname))
2915 continue;
2916 iF.F = xfopen_stdin(fname);
2917 setvar_i(intvar[ARGIND], argind);
2852 break; 2918 break;
2853 } 2919 }
2854 } 2920 }
2855#if ENABLE_PLATFORM_MINGW32 2921#if ENABLE_PLATFORM_MINGW32
2856 set_text_mode(rsm.F); 2922 set_text_mode(iF.F);
2857#endif 2923#endif
2858 2924
2859 files_happen = TRUE;
2860 setvar_s(intvar[FILENAME], fname); 2925 setvar_s(intvar[FILENAME], fname);
2861 return &rsm; 2926 input_file_seen = TRUE;
2862#undef rsm 2927 return TRUE;
2863#undef files_happen 2928#undef argind
2929#undef input_file_seen
2864} 2930}
2865 2931
2866#if ENABLE_PLATFORM_MINGW32 2932#if ENABLE_PLATFORM_MINGW32
@@ -2914,6 +2980,7 @@ static var *evaluate(node *op, var *res)
2914 uint32_t opinfo; 2980 uint32_t opinfo;
2915 int opn; 2981 int opn;
2916 node *op1; 2982 node *op1;
2983 var *old_Fields_ptr;
2917 2984
2918 opinfo = op->info; 2985 opinfo = op->info;
2919 opn = (opinfo & OPNMASK); 2986 opn = (opinfo & OPNMASK);
@@ -2922,10 +2989,16 @@ static var *evaluate(node *op, var *res)
2922 debug_printf_eval("opinfo:%08x opn:%08x\n", opinfo, opn); 2989 debug_printf_eval("opinfo:%08x opn:%08x\n", opinfo, opn);
2923 2990
2924 /* execute inevitable things */ 2991 /* execute inevitable things */
2992 old_Fields_ptr = NULL;
2925 if (opinfo & OF_RES1) { 2993 if (opinfo & OF_RES1) {
2926 if ((opinfo & OF_REQUIRED) && !op1) 2994 if ((opinfo & OF_REQUIRED) && !op1)
2927 syntax_error(EMSG_TOO_FEW_ARGS); 2995 syntax_error(EMSG_TOO_FEW_ARGS);
2928 L.v = evaluate(op1, TMPVAR0); 2996 L.v = evaluate(op1, TMPVAR0);
2997 /* Does L.v point to $n variable? */
2998 if ((size_t)(L.v - Fields) < maxfields) {
2999 /* yes, remember where Fields[] is */
3000 old_Fields_ptr = Fields;
3001 }
2929 if (opinfo & OF_STR1) { 3002 if (opinfo & OF_STR1) {
2930 L.s = getvar_s(L.v); 3003 L.s = getvar_s(L.v);
2931 debug_printf_eval("L.s:'%s'\n", L.s); 3004 debug_printf_eval("L.s:'%s'\n", L.s);
@@ -2944,8 +3017,15 @@ static var *evaluate(node *op, var *res)
2944 */ 3017 */
2945 if (opinfo & OF_RES2) { 3018 if (opinfo & OF_RES2) {
2946 R.v = evaluate(op->r.n, TMPVAR1); 3019 R.v = evaluate(op->r.n, TMPVAR1);
2947 //TODO: L.v may be invalid now, set L.v to NULL to catch bugs? 3020 /* Seen in $5=$$5=$0:
2948 //L.v = NULL; 3021 * Evaluation of R.v ($$5=$0 expression)
3022 * made L.v ($5) invalid. It's detected here.
3023 */
3024 if (old_Fields_ptr) {
3025 //if (old_Fields_ptr != Fields)
3026 // debug_printf_eval("L.v moved\n");
3027 L.v += Fields - old_Fields_ptr;
3028 }
2949 if (opinfo & OF_STR2) { 3029 if (opinfo & OF_STR2) {
2950 R.s = getvar_s(R.v); 3030 R.s = getvar_s(R.v);
2951 debug_printf_eval("R.s:'%s'\n", R.s); 3031 debug_printf_eval("R.s:'%s'\n", R.s);
@@ -3111,6 +3191,8 @@ static var *evaluate(node *op, var *res)
3111 3191
3112 /* -- recursive node type -- */ 3192 /* -- recursive node type -- */
3113 3193
3194 case XC( OC_CONST ):
3195 debug_printf_eval("CONST ");
3114 case XC( OC_VAR ): 3196 case XC( OC_VAR ):
3115 debug_printf_eval("VAR\n"); 3197 debug_printf_eval("VAR\n");
3116 L.v = op->l.v; 3198 L.v = op->l.v;
@@ -3154,7 +3236,7 @@ static var *evaluate(node *op, var *res)
3154 /* make sure that we never return a temp var */ 3236 /* make sure that we never return a temp var */
3155 if (L.v == TMPVAR0) 3237 if (L.v == TMPVAR0)
3156 L.v = res; 3238 L.v = res;
3157 /* if source is a temporary string, jusk relink it to dest */ 3239 /* if source is a temporary string, just relink it to dest */
3158 if (R.v == TMPVAR1 3240 if (R.v == TMPVAR1
3159 && !(R.v->type & VF_NUMBER) 3241 && !(R.v->type & VF_NUMBER)
3160 /* Why check !NUMBER? if R.v is a number but has cached R.v->string, 3242 /* Why check !NUMBER? if R.v is a number but has cached R.v->string,
@@ -3240,13 +3322,13 @@ static var *evaluate(node *op, var *res)
3240#endif 3322#endif
3241 } 3323 }
3242 } else { 3324 } else {
3243 if (!iF) 3325 if (!iF.F)
3244 iF = next_input_file(); 3326 next_input_file();
3245 rsm = iF; 3327 rsm = &iF;
3246 } 3328 }
3247 3329
3248 if (!rsm || !rsm->F) { 3330 if (!rsm->F) {
3249 setvar_i(intvar[ERRNO], errno); 3331 setvar_ERRNO();
3250 setvar_i(res, -1); 3332 setvar_i(res, -1);
3251 break; 3333 break;
3252 } 3334 }
@@ -3395,16 +3477,18 @@ static var *evaluate(node *op, var *res)
3395 */ 3477 */
3396 if (rsm->F) 3478 if (rsm->F)
3397 err = rsm->is_pipe ? pclose(rsm->F) : fclose(rsm->F); 3479 err = rsm->is_pipe ? pclose(rsm->F) : fclose(rsm->F);
3398//TODO: fix this case:
3399// $ awk 'BEGIN { print close(""); print ERRNO }'
3400// -1
3401// close of redirection that was never opened
3402// (we print 0, 0)
3403 free(rsm->buffer); 3480 free(rsm->buffer);
3404 hash_remove(fdhash, L.s); 3481 hash_remove(fdhash, L.s);
3482 } else {
3483 err = -1;
3484 /* gawk 'BEGIN { print close(""); print ERRNO }'
3485 * -1
3486 * close of redirection that was never opened
3487 */
3488 errno = ENOENT;
3405 } 3489 }
3406 if (err) 3490 if (err)
3407 setvar_i(intvar[ERRNO], errno); 3491 setvar_ERRNO();
3408 R_d = (double)err; 3492 R_d = (double)err;
3409 break; 3493 break;
3410 } 3494 }
@@ -3584,8 +3668,6 @@ static var *evaluate(node *op, var *res)
3584#undef sreg 3668#undef sreg
3585} 3669}
3586 3670
3587/* -------- main & co. -------- */
3588
3589static int awk_exit(void) 3671static int awk_exit(void)
3590{ 3672{
3591 unsigned i; 3673 unsigned i;
@@ -3678,7 +3760,7 @@ int awk_main(int argc UNUSED_PARAM, char **argv)
3678 setvar_s(intvar[FS], opt_F); 3760 setvar_s(intvar[FS], opt_F);
3679 } 3761 }
3680 while (list_v) { 3762 while (list_v) {
3681 if (!is_assignment(llist_pop(&list_v))) 3763 if (!try_to_assign(llist_pop(&list_v)))
3682 bb_show_usage(); 3764 bb_show_usage();
3683 } 3765 }
3684 3766
@@ -3695,6 +3777,8 @@ int awk_main(int argc UNUSED_PARAM, char **argv)
3695 _setmode(fd, _O_TEXT); 3777 _setmode(fd, _O_TEXT);
3696#endif 3778#endif
3697 s = xmalloc_read(fd, NULL); /* it's NUL-terminated */ 3779 s = xmalloc_read(fd, NULL); /* it's NUL-terminated */
3780 if (!s)
3781 bb_perror_msg_and_die("read error from '%s'", g_progname);
3698 close(fd); 3782 close(fd);
3699 parse_program(s); 3783 parse_program(s);
3700 free(s); 3784 free(s);
@@ -3740,15 +3824,14 @@ int awk_main(int argc UNUSED_PARAM, char **argv)
3740 awk_exit(); 3824 awk_exit();
3741 3825
3742 /* input file could already be opened in BEGIN block */ 3826 /* input file could already be opened in BEGIN block */
3743 if (!iF) 3827 if (!iF.F)
3744 iF = next_input_file(); 3828 goto next_file; /* no, it wasn't, go try opening */
3745 3829 /* Iterate over input files */
3746 /* passing through input files */ 3830 for (;;) {
3747 while (iF) {
3748 nextfile = FALSE; 3831 nextfile = FALSE;
3749 setvar_i(intvar[FNR], 0); 3832 setvar_i(intvar[FNR], 0);
3750 3833
3751 while ((i = awk_getline(iF, intvar[F0])) > 0) { 3834 while ((i = awk_getline(&iF, intvar[F0])) > 0) {
3752 nextrec = FALSE; 3835 nextrec = FALSE;
3753 incvar(intvar[NR]); 3836 incvar(intvar[NR]);
3754 incvar(intvar[FNR]); 3837 incvar(intvar[FNR]);
@@ -3757,11 +3840,11 @@ int awk_main(int argc UNUSED_PARAM, char **argv)
3757 if (nextfile) 3840 if (nextfile)
3758 break; 3841 break;
3759 } 3842 }
3760
3761 if (i < 0) 3843 if (i < 0)
3762 syntax_error(strerror(errno)); 3844 syntax_error(strerror(errno));
3763 3845 next_file:
3764 iF = next_input_file(); 3846 if (!next_input_file())
3847 break;
3765 } 3848 }
3766 3849
3767 awk_exit(); 3850 awk_exit();
diff --git a/include/dump.h b/include/dump.h
index 10fc5d900..c6763a64d 100644
--- a/include/dump.h
+++ b/include/dump.h
@@ -33,9 +33,14 @@ typedef struct dumper_t {
33 int dump_length; /* max bytes to read */ 33 int dump_length; /* max bytes to read */
34 smallint dump_vflag; /*enum dump_vflag_t*/ 34 smallint dump_vflag; /*enum dump_vflag_t*/
35 FS *fshead; 35 FS *fshead;
36#if ENABLE_OD
37 const char *od_eofstring;
38#endif
39#if ENABLE_XXD
36 const char *xxd_eofstring; 40 const char *xxd_eofstring;
37 off_t address; /* address/offset in stream */
38 long long xxd_displayoff; 41 long long xxd_displayoff;
42#endif
43 off_t address; /* address/offset in stream */
39} dumper_t; 44} dumper_t;
40 45
41dumper_t* alloc_dumper(void) FAST_FUNC; 46dumper_t* alloc_dumper(void) FAST_FUNC;
diff --git a/libbb/dump.c b/libbb/dump.c
index d24057325..ffc46f6a7 100644
--- a/libbb/dump.c
+++ b/libbb/dump.c
@@ -50,8 +50,10 @@ typedef struct priv_dumper_t {
50static const char dot_flags_width_chars[] ALIGN1 = ".#-+ 0123456789"; 50static const char dot_flags_width_chars[] ALIGN1 = ".#-+ 0123456789";
51 51
52static const char size_conv_str[] ALIGN1 = 52static const char size_conv_str[] ALIGN1 =
53"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG"; 53"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\x8""cdiouxXeEfgG";
54 54/* c d i o u x X e E f g G - bytes contain 'bcnt' for the type */
55#define SCS_OFS 12
56#define float_convs (size_conv_str + SCS_OFS + sizeof("cdiouxX")-1)
55static const char int_convs[] ALIGN1 = "diouxX"; 57static const char int_convs[] ALIGN1 = "diouxX";
56 58
57dumper_t* FAST_FUNC alloc_dumper(void) 59dumper_t* FAST_FUNC alloc_dumper(void)
@@ -94,7 +96,7 @@ static NOINLINE int bb_dump_size(FS *fs)
94 while (isdigit(*++fmt)) 96 while (isdigit(*++fmt))
95 continue; 97 continue;
96 } 98 }
97 p = strchr(size_conv_str + 12, *fmt); 99 p = strchr(size_conv_str + SCS_OFS, *fmt);
98 if (!p) { 100 if (!p) {
99 if (*fmt == 's') { 101 if (*fmt == 's') {
100 bcnt += prec; 102 bcnt += prec;
@@ -106,7 +108,7 @@ static NOINLINE int bb_dump_size(FS *fs)
106 } 108 }
107 } 109 }
108 } else { 110 } else {
109 bcnt += p[-12]; 111 bcnt += p[-SCS_OFS];
110 } 112 }
111 } 113 }
112 cur_size += bcnt * fu->reps; 114 cur_size += bcnt * fu->reps;
@@ -193,6 +195,10 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
193 195
194 ++p2; 196 ++p2;
195 ++p1; 197 ++p1;
198 if (*p1 == 'l') { /* %lld etc */
199 ++p2;
200 ++p1;
201 }
196 DO_INT_CONV: 202 DO_INT_CONV:
197 e = strchr(int_convs, *p1); /* "diouxX"? */ 203 e = strchr(int_convs, *p1); /* "diouxX"? */
198 if (!e) 204 if (!e)
@@ -200,13 +206,13 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
200 pr->flags = F_INT; 206 pr->flags = F_INT;
201 if (e > int_convs + 1) /* not d or i? */ 207 if (e > int_convs + 1) /* not d or i? */
202 pr->flags = F_UINT; 208 pr->flags = F_UINT;
203 byte_count_str = "\004\002\001"; 209 byte_count_str = "\010\004\002\001";
204 goto DO_BYTE_COUNT; 210 goto DO_BYTE_COUNT;
205 } else 211 } else
206 if (strchr(int_convs, *p1)) { /* %d etc */ 212 if (strchr(int_convs, *p1)) { /* %d etc */
207 goto DO_INT_CONV; 213 goto DO_INT_CONV;
208 } else 214 } else
209 if (strchr("eEfgG", *p1)) { /* floating point */ 215 if (strchr(float_convs, *p1)) { /* floating point */
210 pr->flags = F_DBL; 216 pr->flags = F_DBL;
211 byte_count_str = "\010\004"; 217 byte_count_str = "\010\004";
212 goto DO_BYTE_COUNT; 218 goto DO_BYTE_COUNT;
@@ -244,7 +250,7 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
244 pr->flags = F_P; 250 pr->flags = F_P;
245 *p1 = 'c'; 251 *p1 = 'c';
246 goto DO_BYTE_COUNT_1; 252 goto DO_BYTE_COUNT_1;
247 case 'u': /* %_p: chars, 'nul', 'esc' etc for nonprintable */ 253 case 'u': /* %_u: chars, 'nul', 'esc' etc for nonprintable */
248 pr->flags = F_U; 254 pr->flags = F_U;
249 /* *p1 = 'c'; set in conv_u */ 255 /* *p1 = 'c'; set in conv_u */
250 goto DO_BYTE_COUNT_1; 256 goto DO_BYTE_COUNT_1;
@@ -324,8 +330,7 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
324 p2 = NULL; 330 p2 = NULL;
325 for (p1 = pr->fmt; *p1; ++p1) 331 for (p1 = pr->fmt; *p1; ++p1)
326 p2 = isspace(*p1) ? p1 : NULL; 332 p2 = isspace(*p1) ? p1 : NULL;
327 if (p2) 333 pr->nospace = p2;
328 pr->nospace = p2;
329 } 334 }
330 } 335 }
331} 336}
@@ -509,7 +514,7 @@ static void bpad(PR *pr)
509 514
510static const char conv_str[] ALIGN1 = 515static const char conv_str[] ALIGN1 =
511 "\0" "\\""0""\0" 516 "\0" "\\""0""\0"
512 "\007""\\""a""\0" /* \a */ 517 "\007""\\""a""\0"
513 "\b" "\\""b""\0" 518 "\b" "\\""b""\0"
514 "\f" "\\""f""\0" 519 "\f" "\\""f""\0"
515 "\n" "\\""n""\0" 520 "\n" "\\""n""\0"
@@ -549,10 +554,12 @@ static void conv_u(PR *pr, unsigned char *p)
549 static const char list[] ALIGN1 = 554 static const char list[] ALIGN1 =
550 "nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0" 555 "nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0"
551 "bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_" 556 "bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_"
552 "dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0" 557 "dle\0dc1\0dc2\0dc3\0dc4\0nak\0syn\0etb\0"
553 "can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us"; 558 "can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us";
559 /* NB: bug: od uses %_u to implement -a,
560 * but it should use "nl", not "lf", for char #10.
561 */
554 562
555 /* od used nl, not lf */
556 if (*p <= 0x1f) { 563 if (*p <= 0x1f) {
557 *pr->cchar = 's'; 564 *pr->cchar = 's';
558 printf(pr->fmt, list + (4 * (int)*p)); 565 printf(pr->fmt, list + (4 * (int)*p));
@@ -571,7 +578,6 @@ static void conv_u(PR *pr, unsigned char *p)
571static NOINLINE void display(priv_dumper_t* dumper) 578static NOINLINE void display(priv_dumper_t* dumper)
572{ 579{
573 unsigned char *bp; 580 unsigned char *bp;
574 unsigned char savech = '\0';
575 581
576 while ((bp = get(dumper)) != NULL) { 582 while ((bp = get(dumper)) != NULL) {
577 FS *fs; 583 FS *fs;
@@ -592,24 +598,41 @@ static NOINLINE void display(priv_dumper_t* dumper)
592 PR *pr; 598 PR *pr;
593 for (pr = fu->nextpr; pr; dumper->pub.address += pr->bcnt, 599 for (pr = fu->nextpr; pr; dumper->pub.address += pr->bcnt,
594 bp += pr->bcnt, pr = pr->nextpr) { 600 bp += pr->bcnt, pr = pr->nextpr) {
601 unsigned char savech;
602
595 if (dumper->eaddress 603 if (dumper->eaddress
596 && dumper->pub.address >= dumper->eaddress 604 && dumper->pub.address >= dumper->eaddress
597 ) { 605 ) {
606#if ENABLE_XXD
598 if (dumper->pub.xxd_eofstring) { 607 if (dumper->pub.xxd_eofstring) {
599 /* xxd support: requested to not pad incomplete blocks */ 608 /* xxd support: requested to not pad incomplete blocks */
600 fputs_stdout(dumper->pub.xxd_eofstring); 609 fputs_stdout(dumper->pub.xxd_eofstring);
601 return; 610 return;
602 } 611 }
612#endif
613#if ENABLE_OD
614 if (dumper->pub.od_eofstring) {
615 /* od support: requested to not pad incomplete blocks */
616 /* ... but do print final offset */
617 fputs_stdout(dumper->pub.od_eofstring);
618 goto endfu;
619 }
620#endif
603 if (!(pr->flags & (F_TEXT | F_BPAD))) 621 if (!(pr->flags & (F_TEXT | F_BPAD)))
604 bpad(pr); 622 bpad(pr);
605 } 623 }
624 savech = '\0';
606 if (cnt == 1 && pr->nospace) { 625 if (cnt == 1 && pr->nospace) {
607 savech = *pr->nospace; 626 savech = *pr->nospace;
608 *pr->nospace = '\0'; 627 *pr->nospace = '\0';
609 } 628 }
610 switch (pr->flags) { 629 switch (pr->flags) {
611 case F_ADDRESS: 630 case F_ADDRESS:
612 printf(pr->fmt, (unsigned long long) dumper->pub.address + dumper->pub.xxd_displayoff); 631 printf(pr->fmt, (unsigned long long) dumper->pub.address
632#if ENABLE_XXD
633 + dumper->pub.xxd_displayoff
634#endif
635 );
613 break; 636 break;
614 case F_BPAD: 637 case F_BPAD:
615 printf(pr->fmt, ""); 638 printf(pr->fmt, "");
@@ -637,22 +660,32 @@ static NOINLINE void display(priv_dumper_t* dumper)
637 break; 660 break;
638 } 661 }
639 case F_INT: { 662 case F_INT: {
640 int ival; 663 union {
641 short sval; 664 int16_t ival16;
665 int32_t ival32;
666 int64_t ival64;
667 } u;
668 int value = (signed char)*bp;
642 669
643 switch (pr->bcnt) { 670 switch (pr->bcnt) {
644 case 1: 671 case 1:
645 printf(pr->fmt, (int) *bp);
646 break; 672 break;
647 case 2: 673 case 2:
648 memcpy(&sval, bp, sizeof(sval)); 674 move_from_unaligned16(u.ival16, bp);
649 printf(pr->fmt, (int) sval); 675 value = u.ival16;
650 break; 676 break;
651 case 4: 677 case 4:
652 memcpy(&ival, bp, sizeof(ival)); 678 move_from_unaligned32(u.ival32, bp);
653 printf(pr->fmt, ival); 679 value = u.ival32;
654 break; 680 break;
681 case 8:
682 move_from_unaligned64(u.ival64, bp);
683//A hack. Users _must_ use %llX formats to not truncate high bits
684 printf(pr->fmt, (long long)u.ival64);
685 goto skip;
655 } 686 }
687 printf(pr->fmt, value);
688 skip:
656 break; 689 break;
657 } 690 }
658 case F_P: 691 case F_P:
@@ -662,32 +695,29 @@ static NOINLINE void display(priv_dumper_t* dumper)
662 printf(pr->fmt, (char *) bp); 695 printf(pr->fmt, (char *) bp);
663 break; 696 break;
664 case F_TEXT: 697 case F_TEXT:
665 printf(pr->fmt); 698 fputs_stdout(pr->fmt);
666 break; 699 break;
667 case F_U: 700 case F_U:
668 conv_u(pr, bp); 701 conv_u(pr, bp);
669 break; 702 break;
670 case F_UINT: { 703 case F_UINT: {
671 unsigned ival; 704 unsigned value = (unsigned char)*bp;
672 unsigned short sval;
673
674 switch (pr->bcnt) { 705 switch (pr->bcnt) {
675 case 1: 706 case 1:
676 printf(pr->fmt, (unsigned) *bp);
677 break; 707 break;
678 case 2: 708 case 2:
679 memcpy(&sval, bp, sizeof(sval)); 709 move_from_unaligned16(value, bp);
680 printf(pr->fmt, (unsigned) sval);
681 break; 710 break;
682 case 4: 711 case 4:
683 memcpy(&ival, bp, sizeof(ival)); 712 move_from_unaligned32(value, bp);
684 printf(pr->fmt, ival);
685 break; 713 break;
714 /* case 8: no users yet */
686 } 715 }
716 printf(pr->fmt, value);
687 break; 717 break;
688 } 718 }
689 } 719 }
690 if (cnt == 1 && pr->nospace) { 720 if (savech) {
691 *pr->nospace = savech; 721 *pr->nospace = savech;
692 } 722 }
693 } 723 }
@@ -695,7 +725,7 @@ static NOINLINE void display(priv_dumper_t* dumper)
695 } 725 }
696 } 726 }
697 } 727 }
698 728 IF_OD(endfu:)
699 if (dumper->endfu) { 729 if (dumper->endfu) {
700 PR *pr; 730 PR *pr;
701 /* 731 /*
@@ -711,10 +741,14 @@ static NOINLINE void display(priv_dumper_t* dumper)
711 for (pr = dumper->endfu->nextpr; pr; pr = pr->nextpr) { 741 for (pr = dumper->endfu->nextpr; pr; pr = pr->nextpr) {
712 switch (pr->flags) { 742 switch (pr->flags) {
713 case F_ADDRESS: 743 case F_ADDRESS:
714 printf(pr->fmt, (unsigned long long) dumper->eaddress + dumper->pub.xxd_displayoff); 744 printf(pr->fmt, (unsigned long long) dumper->eaddress
745#if ENABLE_XXD
746 + dumper->pub.xxd_displayoff
747#endif
748 );
715 break; 749 break;
716 case F_TEXT: 750 case F_TEXT:
717 printf(pr->fmt); 751 fputs_stdout(pr->fmt);
718 break; 752 break;
719 } 753 }
720 } 754 }
diff --git a/networking/tunctl.c b/networking/tunctl.c
index 97e6917aa..28571ae7f 100644
--- a/networking/tunctl.c
+++ b/networking/tunctl.c
@@ -124,8 +124,7 @@ int tunctl_main(int argc UNUSED_PARAM, char **argv)
124 if (opts & OPT_b) { 124 if (opts & OPT_b) {
125 puts(ifr.ifr_name); 125 puts(ifr.ifr_name);
126 } else { 126 } else {
127 printf("Set '%s' %spersistent", ifr.ifr_name, ""); 127 printf("Set '%s' persistent and owned by uid %ld", ifr.ifr_name, user);
128 printf(" and owned by uid %ld", user);
129 if (group != -1) 128 if (group != -1)
130 printf(" gid %ld", group); 129 printf(" gid %ld", group);
131 bb_putchar('\n'); 130 bb_putchar('\n');
diff --git a/shell/ash_test/ash-misc/elif1.right b/shell/ash_test/ash-misc/elif1.right
new file mode 100644
index 000000000..36dc59fed
--- /dev/null
+++ b/shell/ash_test/ash-misc/elif1.right
@@ -0,0 +1,4 @@
1ELIF1
2ELIF2
3ELIF THEN
4Ok:0
diff --git a/shell/ash_test/ash-misc/elif1.tests b/shell/ash_test/ash-misc/elif1.tests
new file mode 100755
index 000000000..77b8a25ea
--- /dev/null
+++ b/shell/ash_test/ash-misc/elif1.tests
@@ -0,0 +1,6 @@
1if false; then
2 :
3elif echo 'ELIF1'; echo 'ELIF2'; then
4 echo "ELIF THEN"
5fi
6echo "Ok:$?"
diff --git a/shell/ash_test/ash-misc/elif2.right b/shell/ash_test/ash-misc/elif2.right
new file mode 100644
index 000000000..8f2851f91
--- /dev/null
+++ b/shell/ash_test/ash-misc/elif2.right
@@ -0,0 +1,2 @@
1THEN
2Ok:0
diff --git a/shell/ash_test/ash-misc/elif2.tests b/shell/ash_test/ash-misc/elif2.tests
new file mode 100755
index 000000000..3e5876f05
--- /dev/null
+++ b/shell/ash_test/ash-misc/elif2.tests
@@ -0,0 +1,8 @@
1if true; then
2 echo "THEN"
3elif echo "ELIF false"; false; then
4 echo "ELIF THEN"
5else
6 echo "ELSE"
7fi
8echo "Ok:$?"
diff --git a/shell/hush.c b/shell/hush.c
index 810dafd35..cdaa67a3b 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -1061,6 +1061,7 @@ static int builtin_export(char **argv) FAST_FUNC;
1061#if ENABLE_HUSH_READONLY 1061#if ENABLE_HUSH_READONLY
1062static int builtin_readonly(char **argv) FAST_FUNC; 1062static int builtin_readonly(char **argv) FAST_FUNC;
1063#endif 1063#endif
1064static int builtin_false(char **argv) FAST_FUNC;
1064#if ENABLE_HUSH_JOB 1065#if ENABLE_HUSH_JOB
1065static int builtin_fg_bg(char **argv) FAST_FUNC; 1066static int builtin_fg_bg(char **argv) FAST_FUNC;
1066static int builtin_jobs(char **argv) FAST_FUNC; 1067static int builtin_jobs(char **argv) FAST_FUNC;
@@ -1161,6 +1162,7 @@ static const struct built_in_command bltins1[] ALIGN_PTR = {
1161#if ENABLE_HUSH_EXPORT 1162#if ENABLE_HUSH_EXPORT
1162 BLTIN("export" , builtin_export , "Set environment variables"), 1163 BLTIN("export" , builtin_export , "Set environment variables"),
1163#endif 1164#endif
1165 BLTIN("false" , builtin_false , NULL),
1164#if ENABLE_HUSH_JOB 1166#if ENABLE_HUSH_JOB
1165 BLTIN("fg" , builtin_fg_bg , "Bring job to foreground"), 1167 BLTIN("fg" , builtin_fg_bg , "Bring job to foreground"),
1166#endif 1168#endif
@@ -9758,7 +9760,7 @@ static int run_list(struct pipe *pi)
9758 smallint last_rword; /* ditto */ 9760 smallint last_rword; /* ditto */
9759#endif 9761#endif
9760 9762
9761 debug_printf_exec("run_list start lvl %d\n", G.run_list_level); 9763 debug_printf_exec("run_list lvl %d start\n", G.run_list_level);
9762 debug_enter(); 9764 debug_enter();
9763 9765
9764#if ENABLE_HUSH_LOOPS 9766#if ENABLE_HUSH_LOOPS
@@ -9817,7 +9819,7 @@ static int run_list(struct pipe *pi)
9817 break; 9819 break;
9818 9820
9819 IF_HAS_KEYWORDS(rword = pi->res_word;) 9821 IF_HAS_KEYWORDS(rword = pi->res_word;)
9820 debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n", 9822 debug_printf_exec(": rword:%d cond_code:%d last_rword:%d\n",
9821 rword, cond_code, last_rword); 9823 rword, cond_code, last_rword);
9822 9824
9823 sv_errexit_depth = G.errexit_depth; 9825 sv_errexit_depth = G.errexit_depth;
@@ -9851,23 +9853,29 @@ static int run_list(struct pipe *pi)
9851 } 9853 }
9852 } 9854 }
9853 last_followup = pi->followup; 9855 last_followup = pi->followup;
9854 IF_HAS_KEYWORDS(last_rword = rword;)
9855#if ENABLE_HUSH_IF 9856#if ENABLE_HUSH_IF
9856 if (cond_code) { 9857 if (cond_code != 0) {
9857 if (rword == RES_THEN) { 9858 if (rword == RES_THEN) {
9858 /* if false; then ... fi has exitcode 0! */ 9859 /* if false; then ... fi has exitcode 0! */
9859 G.last_exitcode = rcode = EXIT_SUCCESS; 9860 G.last_exitcode = rcode = EXIT_SUCCESS;
9860 /* "if <false> THEN cmd": skip cmd */ 9861 /* "if <false> THEN cmd": skip cmd */
9862 debug_printf_exec("skipped THEN cmd because IF condition was false\n");
9863 last_rword = rword;
9861 continue; 9864 continue;
9862 } 9865 }
9863 } else { 9866 } else {
9864 if (rword == RES_ELSE || rword == RES_ELIF) { 9867 if (rword == RES_ELSE
9868 || (rword == RES_ELIF && last_rword != RES_ELIF)
9869 ) {
9865 /* "if <true> then ... ELSE/ELIF cmd": 9870 /* "if <true> then ... ELSE/ELIF cmd":
9866 * skip cmd and all following ones */ 9871 * skip cmd and all following ones */
9872 debug_printf_exec("skipped ELSE/ELIF branch because IF condition was true\n");
9867 break; 9873 break;
9868 } 9874 }
9875 //if (rword == RES_THEN): "if <true> THEN cmd", run cmd (fall through)
9869 } 9876 }
9870#endif 9877#endif
9878 IF_HAS_KEYWORDS(last_rword = rword;)
9871#if ENABLE_HUSH_LOOPS 9879#if ENABLE_HUSH_LOOPS
9872 if (rword == RES_FOR) { /* && pi->num_cmds - always == 1 */ 9880 if (rword == RES_FOR) { /* && pi->num_cmds - always == 1 */
9873 if (!for_lcur) { 9881 if (!for_lcur) {
@@ -9943,7 +9951,7 @@ static int run_list(struct pipe *pi)
9943 ); 9951 );
9944 /* TODO: which FNM_xxx flags to use? */ 9952 /* TODO: which FNM_xxx flags to use? */
9945 cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0); 9953 cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0);
9946 debug_printf_exec("fnmatch(pattern:'%s',str:'%s'):%d\n", 9954 debug_printf_exec("cond_code=fnmatch(pattern:'%s',str:'%s'):%d\n",
9947 pattern, case_word, cond_code); 9955 pattern, case_word, cond_code);
9948 free(pattern); 9956 free(pattern);
9949 if (cond_code == 0) { 9957 if (cond_code == 0) {
@@ -10069,8 +10077,10 @@ static int run_list(struct pipe *pi)
10069 10077
10070 /* Analyze how result affects subsequent commands */ 10078 /* Analyze how result affects subsequent commands */
10071#if ENABLE_HUSH_IF 10079#if ENABLE_HUSH_IF
10072 if (rword == RES_IF || rword == RES_ELIF) 10080 if (rword == RES_IF || rword == RES_ELIF) {
10081 debug_printf_exec("cond_code=rcode:%d\n", rcode);
10073 cond_code = rcode; 10082 cond_code = rcode;
10083 }
10074#endif 10084#endif
10075 check_jobs_and_continue: 10085 check_jobs_and_continue:
10076 checkjobs(NULL, 0 /*(no pid to wait for)*/); 10086 checkjobs(NULL, 0 /*(no pid to wait for)*/);
@@ -10111,7 +10121,7 @@ static int run_list(struct pipe *pi)
10111 free(case_word); 10121 free(case_word);
10112#endif 10122#endif
10113 debug_leave(); 10123 debug_leave();
10114 debug_printf_exec("run_list lvl %d return %d\n", G.run_list_level + 1, rcode); 10124 debug_printf_exec("run_list lvl %d return %d\n", G.run_list_level, rcode);
10115 return rcode; 10125 return rcode;
10116} 10126}
10117 10127
@@ -10823,6 +10833,11 @@ static int FAST_FUNC builtin_true(char **argv UNUSED_PARAM)
10823 return 0; 10833 return 0;
10824} 10834}
10825 10835
10836static int FAST_FUNC builtin_false(char **argv UNUSED_PARAM)
10837{
10838 return 1;
10839}
10840
10826#if ENABLE_HUSH_TEST || ENABLE_HUSH_ECHO || ENABLE_HUSH_PRINTF || ENABLE_HUSH_KILL 10841#if ENABLE_HUSH_TEST || ENABLE_HUSH_ECHO || ENABLE_HUSH_PRINTF || ENABLE_HUSH_KILL
10827static NOINLINE int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv)) 10842static NOINLINE int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv))
10828{ 10843{
diff --git a/shell/hush_test/hush-misc/elif1.right b/shell/hush_test/hush-misc/elif1.right
new file mode 100644
index 000000000..36dc59fed
--- /dev/null
+++ b/shell/hush_test/hush-misc/elif1.right
@@ -0,0 +1,4 @@
1ELIF1
2ELIF2
3ELIF THEN
4Ok:0
diff --git a/shell/hush_test/hush-misc/elif1.tests b/shell/hush_test/hush-misc/elif1.tests
new file mode 100755
index 000000000..77b8a25ea
--- /dev/null
+++ b/shell/hush_test/hush-misc/elif1.tests
@@ -0,0 +1,6 @@
1if false; then
2 :
3elif echo 'ELIF1'; echo 'ELIF2'; then
4 echo "ELIF THEN"
5fi
6echo "Ok:$?"
diff --git a/shell/hush_test/hush-misc/elif2.right b/shell/hush_test/hush-misc/elif2.right
new file mode 100644
index 000000000..8f2851f91
--- /dev/null
+++ b/shell/hush_test/hush-misc/elif2.right
@@ -0,0 +1,2 @@
1THEN
2Ok:0
diff --git a/shell/hush_test/hush-misc/elif2.tests b/shell/hush_test/hush-misc/elif2.tests
new file mode 100755
index 000000000..3e5876f05
--- /dev/null
+++ b/shell/hush_test/hush-misc/elif2.tests
@@ -0,0 +1,8 @@
1if true; then
2 echo "THEN"
3elif echo "ELIF false"; false; then
4 echo "ELIF THEN"
5else
6 echo "ELSE"
7fi
8echo "Ok:$?"
diff --git a/testsuite/awk.tests b/testsuite/awk.tests
index 11beb1b10..0a84dc13e 100755
--- a/testsuite/awk.tests
+++ b/testsuite/awk.tests
@@ -499,4 +499,120 @@ testing 'awk backslash+CRLF eaten with no trace' \
499 'BEGIN { printf "Hello\\\r\n world\\n" }\n' 499 'BEGIN { printf "Hello\\\r\n world\\n" }\n'
500SKIP= 500SKIP=
501 501
502# User-supplied bug (SEGV) example, was causing use-after-realloc
503testing 'awk assign while assign' \
504 "awk '\$5=\$\$5=\$0'; echo \$?" \
505 "\
506─ process timing ────────────────────────────────────┬─ ─ process timing ────────────────────────────────────┬─ overall results ────┐ results ────┐
507│ run time : │ run time : 0 days, 0 hrs, 0 min, 56 sec │ cycles done : 0 │ days, 0 hrs, 0 min, 56 sec │ cycles done : 0 │
508│ last new find │ last new find : 0 days, 0 hrs, 0 min, 1 sec │ corpus count : 208 │ 0 days, 0 hrs, 0 min, 1 sec │ corpus count : 208 │
509│last saved crash : │last saved crash : none seen yet │saved crashes : 0 │ seen yet │saved crashes : 0 │
510│ last saved hang │ last saved hang : none seen yet │ saved hangs : 0 │ none seen yet │ saved hangs : 0 │
511├─ cycle progress ─────────────────────┬─ ├─ cycle progress ─────────────────────┬─ map coverage┴──────────────────────┤ coverage┴──────────────────────┤
512│ now processing : │ now processing : 184.1 (88.5%) │ map density : 0.30% / 0.52% │ (88.5%) │ map density : 0.30% / 0.52% │ │ now processing : 184.1 (88.5%) │ map density : 0.30% / 0.52% │
513│ runs timed out │ runs timed out : 0 (0.00%) │ count coverage : 2.18 bits/tuple │ 0 (0.00%) │ count coverage : 2.18 bits/tuple │
514├─ stage progress ─────────────────────┼─ ├─ stage progress ─────────────────────┼─ findings in depth ─────────────────┤ in depth ─────────────────┤
515│ now trying : │ now trying : havoc │ favored items : 43 (20.67%) │ │ favored items : 43 (20.67%) │
516│ stage execs : │ stage execs : 11.2k/131k (8.51%) │ new edges on : 52 (25.00%) │ (8.51%) │ new edges on │ stage execs : 11.2k/131k (8.51%) │ new edges on : 52 (25.00%) │ 52 (25.00%) │
517│ total execs : │ total execs : 179k │ total crashes : 0 (0 saved) │ │ total crashes : 0 (0 saved) │ │ total execs : 179k │ total crashes : 0 (0 saved) │
518│ exec speed : │ exec speed : 3143/sec │ total tmouts : 0 (0 saved) │ │ total tmouts : 0 (0 saved) │ │ exec speed : 3143/sec │ total tmouts : 0 (0 saved) │
519├─ fuzzing strategy yields ├─ fuzzing strategy yields ────────────┴─────────────┬─ item geometry ───────┤ item geometry ───────┤
520│ bit flips : │ bit flips : 11/648, 4/638, 5/618 │ levels : 4 │ 4/638, 5/618 │ levels : │ bit flips : 11/648, 4/638, 5/618 │ levels : 4 │ │
521│ byte flips : │ byte flips : 0/81, 0/71, 0/52 │ pending : 199 │ 0/71, 0/52 │ pending : 199 │
522│ arithmetics : 11/4494, │ arithmetics : 11/4494, 0/1153, 0/0 │ pend fav : 35 │ 0/0 │ pend fav : 35 │
523│ known ints : 1/448, 0/1986, 0/2288 │ own finds : 207 │ known ints : │ known ints : 1/448, 0/1986, 0/2288 │ own finds : 207 │ 0/1986, 0/2288 │ own finds : 207 │
524│ dictionary : 0/0, │ dictionary : 0/0, 0/0, 0/0, 0/0 │ imported : 0 │ 0/0, 0/0 │ imported : 0 │
525│havoc/splice : 142/146k, 23/7616 │havoc/splice : 142/146k, 23/7616 │ stability : 100.00% │ stability : 100.00% │
526│py/custom/rq : unused, unused, │py/custom/rq : unused, unused, unused, unused ├───────────────────────┘ unused ├───────────────────────┘
527│ trim/eff : 57.02%/26, │ trim/eff : 57.02%/26, 0.00% │ [cpu000:100%] │ [cpu000:100%]
528└────────────────────────────────────────────────────┘^C └────────────────────────────────────────────────────┘^C
5290
530" \
531 "" \
532 "\
533─ process timing ────────────────────────────────────┬─ overall results ────┐
534│ run time : 0 days, 0 hrs, 0 min, 56 sec │ cycles done : 0 │
535│ last new find : 0 days, 0 hrs, 0 min, 1 sec │ corpus count : 208 │
536│last saved crash : none seen yet │saved crashes : 0 │
537│ last saved hang : none seen yet │ saved hangs : 0 │
538├─ cycle progress ─────────────────────┬─ map coverage┴──────────────────────┤
539│ now processing : 184.1 (88.5%) │ map density : 0.30% / 0.52% │
540│ runs timed out : 0 (0.00%) │ count coverage : 2.18 bits/tuple │
541├─ stage progress ─────────────────────┼─ findings in depth ─────────────────┤
542│ now trying : havoc │ favored items : 43 (20.67%) │
543│ stage execs : 11.2k/131k (8.51%) │ new edges on : 52 (25.00%) │
544│ total execs : 179k │ total crashes : 0 (0 saved) │
545│ exec speed : 3143/sec │ total tmouts : 0 (0 saved) │
546├─ fuzzing strategy yields ────────────┴─────────────┬─ item geometry ───────┤
547│ bit flips : 11/648, 4/638, 5/618 │ levels : 4 │
548│ byte flips : 0/81, 0/71, 0/52 │ pending : 199 │
549│ arithmetics : 11/4494, 0/1153, 0/0 │ pend fav : 35 │
550│ known ints : 1/448, 0/1986, 0/2288 │ own finds : 207 │
551│ dictionary : 0/0, 0/0, 0/0, 0/0 │ imported : 0 │
552│havoc/splice : 142/146k, 23/7616 │ stability : 100.00% │
553│py/custom/rq : unused, unused, unused, unused ├───────────────────────┘
554│ trim/eff : 57.02%/26, 0.00% │ [cpu000:100%]
555└────────────────────────────────────────────────────┘^C"
556
557# If field separator FS=' ' (default), fields are split only on
558# space or tab or linefeed, NOT other whitespace.
559testing 'awk does not split on CR (char 13)' \
560 "awk '{ \$1=\$0; print }'" \
561 'word1 word2 word3\r word2 word3\r\n' \
562 '' 'word1 word2 word3\r'
563
564testing "awk = has higher precedence than == (despite what gawk manpage claims)" \
565 "awk 'BEGIN { v=1; print 2==v; print 2==v=2; print v; print v=3==3; print v}'" \
566 '0\n1\n2\n1\n3\n' \
567 '' ''
568
569sq="'"
570testing 'awk gensub backslashes \' \
571 'awk '$sq'BEGIN { s="\\"; print "s=" s; print gensub("a", s, "g", "a|a") }'$sq \
572 's=\\
573\\|\\
574' '' ''
575testing 'awk gensub backslashes \\' \
576 'awk '$sq'BEGIN { s="\\\\"; print "s=" s; print gensub("a", s, "g", "a|a") }'$sq \
577 's=\\\\
578\\|\\
579' '' ''
580# gawk 5.1.1 handles trailing unpaired \ inconsistently.
581# If replace string is single \, it is used verbatim,
582# but if it is \\\ (three slashes), gawk uses "\<NUL>" (!!!), not "\\" as you would expect.
583testing 'awk gensub backslashes \\\' \
584 'awk '$sq'BEGIN { s="\\\\\\"; print "s=" s; print gensub("a", s, "g", "a|a") }'$sq \
585 's=\\\\\\
586\\\\|\\\\
587' '' ''
588testing 'awk gensub backslashes \\\\' \
589 'awk '$sq'BEGIN { s="\\\\\\\\"; print "s=" s; print gensub("a", s, "g", "a|a") }'$sq \
590 's=\\\\\\\\
591\\\\|\\\\
592' '' ''
593testing 'awk gensub backslashes \&' \
594 'awk '$sq'BEGIN { s="\\&"; print "s=" s; print gensub("a", s, "g", "a|a") }'$sq \
595 's=\\&
596&|&
597' '' ''
598testing 'awk gensub backslashes \0' \
599 'awk '$sq'BEGIN { s="\\0"; print "s=" s; print gensub("a", s, "g", "a|a") }'$sq \
600 's=\\0
601a|a
602' '' ''
603testing 'awk gensub backslashes \\0' \
604 'awk '$sq'BEGIN { s="\\\\0"; print "s=" s; print gensub("a", s, "g", "a|a") }'$sq \
605 's=\\\\0
606\\0|\\0
607' '' ''
608
609# The "b" in "abc" should not match <b* pattern.
610# Currently we use REG_STARTEND ("This flag is a BSD extension, not present in POSIX")
611# to implement the code to handle this correctly, but if your libc has no REG_STARTEND,
612# the alternative code mishandles this case.
613testing 'awk gsub erroneous word start match' \
614 "awk 'BEGIN { a=\"abc\"; gsub(/\<b*/,\"\",a); print a }'" \
615 'abc\n' \
616 '' ''
617
502exit $FAILCOUNT 618exit $FAILCOUNT
diff --git a/testsuite/hexdump.tests b/testsuite/hexdump.tests
index cfb20187e..be0379cfc 100755
--- a/testsuite/hexdump.tests
+++ b/testsuite/hexdump.tests
@@ -34,4 +34,52 @@ testing "hexdump thinks last full block can match" \
34 '' \ 34 '' \
35 '\0\0\0\0\0\0\0\0\0\0\0\0' 35 '\0\0\0\0\0\0\0\0\0\0\0\0'
36 36
37testing "hexdump -e %3_u" \
38 "hexdump -e '16/1 \" %3_u\" \"\n\"'" \
39 "\
40 nul soh stx etx eot enq ack bel bs ht lf vt ff cr so si
41 dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us
42 p q r s t u v w x y z { | } ~ del
43 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f
44 f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
45" \
46 "" \
47"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"\
48"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"\
49"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"\
50"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"\
51"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"\
52
53testing "hexdump -e /1 %d" \
54 "hexdump -e '16/1 \" %4d\" \"\n\"'" \
55 "\
56 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
57 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
58 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
59 -128 -127 -126 -125 -124 -123 -122 -121 -120 -119 -118 -117 -116 -115 -114 -113
60 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1
61" \
62 "" \
63"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"\
64"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"\
65"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"\
66"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"\
67"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"\
68
69testing "hexdump -e /2 %d" \
70 "hexdump -e '8/2 \" %6d\" \"\n\"'" \
71 "\
72 256 770 1284 1798 2312 2826 3340 3854
73 4368 4882 5396 5910 6424 6938 7452 7966
74 29040 29554 30068 30582 31096 31610 32124 32638
75 -32384 -31870 -31356 -30842 -30328 -29814 -29300 -28786
76 -3600 -3086 -2572 -2058 -1544 -1030 -516 -2
77" \
78 "" \
79"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"\
80"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"\
81"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"\
82"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"\
83"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"\
84
37exit $FAILCOUNT 85exit $FAILCOUNT
diff --git a/testsuite/od.tests b/testsuite/od.tests
index 0880e0d2f..4f245a7e8 100755
--- a/testsuite/od.tests
+++ b/testsuite/od.tests
@@ -6,6 +6,236 @@
6 6
7# testing "test name" "commands" "expected result" "file input" "stdin" 7# testing "test name" "commands" "expected result" "file input" "stdin"
8 8
9input="$(printf '\001\002\003\nABC\xfe')"
10
11little_endian=false
12{ printf '\0\1' | od -s | grep -q 256; } && little_endian=true
13readonly little_endian
14
15$little_endian || SKIP=1
16testing "od (little-endian)" \
17 "od" \
18"\
190000000 001001 005003 041101 177103
200000010
21" \
22 "" "$input"
23SKIP=
24
25optional !DESKTOP
26testing "od -a (!DESKTOP)" \
27 "od -a" \
28"\
290000000 nul soh stx etx eot enq ack bel bs ht lf vt ff cr so si
300000020 dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us
310000040 p q r s t u v w x y z { | } ~ del
320000060 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f
330000100 f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
340000120
35" \
36 "" \
37"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"\
38"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"\
39"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"\
40"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"\
41"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
42SKIP=
43# ^^^ a bit incorrect handling of ctrl ("lf" should be "nl") and high bytes.
44# vvv this output is correct.
45optional DESKTOP
46testing "od -a (DESKTOP)" \
47 "od -a" \
48"\
490000000 nul soh stx etx eot enq ack bel bs ht nl vt ff cr so si
500000020 dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us
510000040 p q r s t u v w x y z { | } ~ del
520000060 nul soh stx etx eot enq ack bel bs ht nl vt ff cr so si
530000100 p q r s t u v w x y z { | } ~ del
540000120
55" \
56 "" \
57"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"\
58"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"\
59"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"\
60"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"\
61"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
62SKIP=
63
64testing "od -B" \
65 "od -B" \
66"\
670000000 001001 005003 041101 177103
680000010
69" \
70 "" "$input"
71SKIP=
72
73$little_endian || SKIP=1
74testing "od -o (little-endian)" \
75 "od -o" \
76"\
770000000 001001 005003 041101 177103
780000010
79" \
80 "" "$input"
81SKIP=
82
83testing "od -b" \
84 "od -b" \
85"\
860000000 001 002 003 012 101 102 103 376
870000010
88" \
89 "" "$input"
90SKIP=
91
92testing "od -c" \
93 "od -c" \
94"\
950000000 001 002 003 \\\\n A B C 376
960000010
97" \
98 "" "$input"
99SKIP=
100
101$little_endian || SKIP=1
102testing "od -d (little-endian)" \
103 "od -d" \
104"\
1050000000 513 2563 16961 65091
1060000010
107" \
108 "" "$input"
109SKIP=
110
111$little_endian || SKIP=1
112testing "od -D (little-endian)" \
113 "od -D" \
114"\
1150000000 167969281 4265820737
1160000010
117" \
118 "" "$input"
119SKIP=
120
121optional !DESKTOP #DESKTOP: unrecognized option: e
122$little_endian || SKIP=1
123testing "od -e (!DESKTOP little-endian)" \
124 "od -e" \
125"\
1260000000 -1.61218556514036e+300
1270000010
128" \
129 "" "$input"
130SKIP=
131
132optional !DESKTOP #DESKTOP: unrecognized option: F
133$little_endian || SKIP=1
134testing "od -F (!DESKTOP little-endian)" \
135 "od -F" \
136"\
1370000000 -1.61218556514036e+300
1380000010
139" \
140 "" "$input"
141SKIP=
142
143$little_endian || SKIP=1
144testing "od -f (little-endian)" \
145 "od -f" \
146"\
1470000000 6.3077975e-33 -6.4885867e+37
1480000010
149" \
150 "" "$input"
151SKIP=
152
153$little_endian || SKIP=1
154testing "od -H (little-endian)" \
155 "od -H" \
156"\
1570000000 0a030201 fe434241
1580000010
159" \
160 "" "$input"
161SKIP=
162
163$little_endian || SKIP=1
164testing "od -X (little-endian)" \
165 "od -X" \
166"\
1670000000 0a030201 fe434241
1680000010
169" \
170 "" "$input"
171SKIP=
172
173$little_endian || SKIP=1
174testing "od -h (little-endian)" \
175 "od -h" \
176"\
1770000000 0201 0a03 4241 fe43
1780000010
179" \
180 "" "$input"
181SKIP=
182
183$little_endian || SKIP=1
184testing "od -x (little-endian)" \
185 "od -x" \
186"\
1870000000 0201 0a03 4241 fe43
1880000010
189" \
190 "" "$input"
191SKIP=
192
193$little_endian || SKIP=1
194testing "od -i (little-endian)" \
195 "od -i" \
196"\
1970000000 167969281 -29146559
1980000010
199" \
200 "" "$input"
201SKIP=
202
203$little_endian || SKIP=1
204testing "od -O (little-endian)" \
205 "od -O" \
206"\
2070000000 01200601001 37620641101
2080000010
209" \
210 "" "$input"
211SKIP=
212
213# 32-bit?
214printf '00000000' | od -l | grep -q '808464432 *808464432' && SKIP=1 #yes, skip
215$little_endian || SKIP=1
216testing "od -I (little-endian)" \
217 "od -I" \
218"\
2190000000 -125183517527965183
2200000010
221" \
222 "" "$input"
223testing "od -L (little-endian)" \
224 "od -L" \
225"\
2260000000 -125183517527965183
2270000010
228" \
229 "" "$input"
230testing "od -l (little-endian)" \
231 "od -l" \
232"\
2330000000 -125183517527965183
2340000010
235" \
236 "" "$input"
237SKIP=
238
9optional DESKTOP 239optional DESKTOP
10testing "od -b" \ 240testing "od -b" \
11 "od -b" \ 241 "od -b" \
diff --git a/testsuite/testing.sh b/testsuite/testing.sh
index f5b756947..95bb46dda 100644
--- a/testsuite/testing.sh
+++ b/testsuite/testing.sh
@@ -56,11 +56,21 @@ optional()
56{ 56{
57 SKIP= 57 SKIP=
58 while test "$1"; do 58 while test "$1"; do
59 case $1 in
60 "!"*)
61 case "${OPTIONFLAGS}" in
62 *:${1#!}:*) SKIP=1; return;;
63 esac
64 shift
65 ;;
66 *)
59 case "${OPTIONFLAGS}" in 67 case "${OPTIONFLAGS}" in
60 *:$1:*) ;; 68 *:$1:*) ;;
61 *) SKIP=1; return ;; 69 *) SKIP=1; return ;;
62 esac 70 esac
63 shift 71 shift
72 ;;
73 esac
64 done 74 done
65} 75}
66 76
diff --git a/util-linux/hexdump.c b/util-linux/hexdump.c
index 307a84803..be4c1964f 100644
--- a/util-linux/hexdump.c
+++ b/util-linux/hexdump.c
@@ -72,14 +72,20 @@ static void bb_dump_addfile(dumper_t *dumper, char *name)
72} 72}
73 73
74static const char *const add_strings[] ALIGN_PTR = { 74static const char *const add_strings[] ALIGN_PTR = {
75 "\"%07.7_ax \"16/1 \"%03o \"\"\n\"", /* b */ 75 "16/1 \" %03o" , /* b */
76 "\"%07.7_ax \"16/1 \"%3_c \"\"\n\"", /* c */ 76 "16/1 \" %3_c" , /* c */
77 "\"%07.7_ax \"8/2 \" %05u \"\"\n\"", /* d */ 77 "8/2 \" %05u" , /* d */
78 "\"%07.7_ax \"8/2 \" %06o \"\"\n\"", /* o */ 78 "8/2 \" %06o" , /* o */
79 "\"%07.7_ax \"8/2 \" %04x \"\"\n\"", /* x */ 79 "8/2 \" %04x", /* x */
80}; 80};
81 81
82static const char add_first[] ALIGN1 = "\"%07.7_Ax\n\""; 82static void add_format(dumper_t *dumper, const char *fmt)
83{
84 char fmtbuf[sizeof("\"%07_ax\"" "%s\"" "\"\n\"") + 16];
85 sprintf(fmtbuf, "\"%%07_ax\"" "%s\"" "\"\n\"", fmt);
86 bb_dump_add(dumper, "\"%07_Ax\n\"");
87 bb_dump_add(dumper, fmtbuf);
88}
83 89
84static const char hexdump_opts[] ALIGN1 = "bcdoxCe:f:n:s:v"; 90static const char hexdump_opts[] ALIGN1 = "bcdoxCe:f:n:s:v";
85 91
@@ -104,15 +110,14 @@ int hexdump_main(int argc, char **argv)
104 if (!p) 110 if (!p)
105 bb_show_usage(); 111 bb_show_usage();
106 if ((p - hexdump_opts) < 5) { 112 if ((p - hexdump_opts) < 5) {
107 bb_dump_add(dumper, add_first); 113 add_format(dumper, add_strings[(int)(p - hexdump_opts)]);
108 bb_dump_add(dumper, add_strings[(int)(p - hexdump_opts)]);
109 } 114 }
110 /* Save a little bit of space below by omitting the 'else's. */ 115 /* Save a little bit of space below by omitting the 'else's. */
111 if (ch == 'C') { 116 if (ch == 'C') {
112 hd_applet: 117 hd_applet:
113 bb_dump_add(dumper, "\"%08.8_Ax\n\""); // final address line after dump 118 bb_dump_add(dumper, "\"%08_Ax\n\""); // final address line after dump
114 //------------------- "address " 8 * "xx " " " 8 * "xx " 119 //------------------- "address " 8 * " xx" " " 8 * " xx"
115 bb_dump_add(dumper, "\"%08.8_ax \"8/1 \"%02x \"\" \"8/1 \"%02x \""); 120 bb_dump_add(dumper, "\"%08_ax \"8/1 \" %02x\"\" \"8/1 \" %02x\"");
116 //------------------- " |ASCII...........|\n" 121 //------------------- " |ASCII...........|\n"
117 bb_dump_add(dumper, "\" |\"16/1 \"%_p\"\"|\n\""); 122 bb_dump_add(dumper, "\" |\"16/1 \"%_p\"\"|\n\"");
118 } 123 }
@@ -139,8 +144,7 @@ int hexdump_main(int argc, char **argv)
139 } 144 }
140 145
141 if (!dumper->fshead) { 146 if (!dumper->fshead) {
142 bb_dump_add(dumper, add_first); 147 add_format(dumper, "8/2 \" %04x");
143 bb_dump_add(dumper, "\"%07.7_ax \"8/2 \"%04x \"\"\n\"");
144 } 148 }
145 149
146 argv += optind; 150 argv += optind;
diff --git a/util-linux/hexdump_xxd.c b/util-linux/hexdump_xxd.c
index 9738a76ad..636cbfeec 100644
--- a/util-linux/hexdump_xxd.c
+++ b/util-linux/hexdump_xxd.c
@@ -285,7 +285,7 @@ int xxd_main(int argc UNUSED_PARAM, char **argv)
285 // output is " 0xXX, 0xXX, 0xXX...", add leading space 285 // output is " 0xXX, 0xXX, 0xXX...", add leading space
286 bb_dump_add(dumper, "\" \""); 286 bb_dump_add(dumper, "\" \"");
287 } else 287 } else
288 bb_dump_add(dumper, "\"%08.8_ax: \""); // "address: " 288 bb_dump_add(dumper, "\"%08_ax: \""); // "address: "
289 } 289 }
290 290
291 if (bytes < 1 || bytes >= cols) { 291 if (bytes < 1 || bytes >= cols) {