diff options
| author | Ron Yorston <rmy@pobox.com> | 2023-06-16 13:02:47 +0100 |
|---|---|---|
| committer | Ron Yorston <rmy@pobox.com> | 2023-06-16 13:02:47 +0100 |
| commit | 4b3d7e62cff6015e1c4b856165efd90cf9182a5c (patch) | |
| tree | 0a59c6a94a47c3e8f1a47b0a3218f9591b842385 | |
| parent | 0627e352656effac8d8e617378e7a68edfce41df (diff) | |
| parent | 2ca39ffd447ca874fcea933194829717d5573247 (diff) | |
| download | busybox-w32-4b3d7e62cff6015e1c4b856165efd90cf9182a5c.tar.gz busybox-w32-4b3d7e62cff6015e1c4b856165efd90cf9182a5c.tar.bz2 busybox-w32-4b3d7e62cff6015e1c4b856165efd90cf9182a5c.zip | |
Merge branch 'busybox' into merge
| -rw-r--r-- | coreutils/od.c | 73 | ||||
| -rw-r--r-- | coreutils/od_bloaty.c | 56 | ||||
| -rw-r--r-- | editors/awk.c | 409 | ||||
| -rw-r--r-- | include/dump.h | 7 | ||||
| -rw-r--r-- | libbb/dump.c | 102 | ||||
| -rw-r--r-- | networking/tunctl.c | 3 | ||||
| -rw-r--r-- | shell/ash_test/ash-misc/elif1.right | 4 | ||||
| -rwxr-xr-x | shell/ash_test/ash-misc/elif1.tests | 6 | ||||
| -rw-r--r-- | shell/ash_test/ash-misc/elif2.right | 2 | ||||
| -rwxr-xr-x | shell/ash_test/ash-misc/elif2.tests | 8 | ||||
| -rw-r--r-- | shell/hush.c | 31 | ||||
| -rw-r--r-- | shell/hush_test/hush-misc/elif1.right | 4 | ||||
| -rwxr-xr-x | shell/hush_test/hush-misc/elif1.tests | 6 | ||||
| -rw-r--r-- | shell/hush_test/hush-misc/elif2.right | 2 | ||||
| -rwxr-xr-x | shell/hush_test/hush-misc/elif2.tests | 8 | ||||
| -rwxr-xr-x | testsuite/awk.tests | 116 | ||||
| -rwxr-xr-x | testsuite/hexdump.tests | 48 | ||||
| -rwxr-xr-x | testsuite/od.tests | 230 | ||||
| -rw-r--r-- | testsuite/testing.sh | 10 | ||||
| -rw-r--r-- | util-linux/hexdump.c | 30 | ||||
| -rw-r--r-- | util-linux/hexdump_xxd.c | 2 |
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) | ||
| 147 | static const char *const add_strings[] ALIGN_PTR = { | 162 | static 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 | ||
| 163 | static const char od_opts[] ALIGN1 = "aBbcDdeFfHhIiLlOoXxv"; | 186 | static const char od_opts[] ALIGN1 = "aBbcDdeFfHhIiLlOoXxsv"; |
| 164 | 187 | ||
| 165 | static const char od_o2si[] ALIGN1 = { | 188 | static 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 | ||
| 172 | int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 195 | int 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 | ||
| 984 | static 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 */ |
| 982 | static var *setvar_u(var *v, const char *value) | 990 | static 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 | ||
| 1016 | static void setvar_ERRNO(void) | ||
| 1017 | { | ||
| 1018 | setvar_i(intvar[ERRNO], errno); | ||
| 1019 | } | ||
| 1020 | |||
| 1008 | static const char *getvar_s(var *v) | 1021 | static 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 | */ |
| 2462 | static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest, int subexp) | 2497 | static 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 */ |
| 2799 | static int is_assignment(const char *expr) | 2852 | static 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 */ |
| 2828 | static rstream *next_input_file(void) | 2881 | static 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 | |||
| 3589 | static int awk_exit(void) | 3671 | static 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 | ||
| 41 | dumper_t* alloc_dumper(void) FAST_FUNC; | 46 | dumper_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 { | |||
| 50 | static const char dot_flags_width_chars[] ALIGN1 = ".#-+ 0123456789"; | 50 | static const char dot_flags_width_chars[] ALIGN1 = ".#-+ 0123456789"; |
| 51 | 51 | ||
| 52 | static const char size_conv_str[] ALIGN1 = | 52 | static 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) | ||
| 55 | static const char int_convs[] ALIGN1 = "diouxX"; | 57 | static const char int_convs[] ALIGN1 = "diouxX"; |
| 56 | 58 | ||
| 57 | dumper_t* FAST_FUNC alloc_dumper(void) | 59 | dumper_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 | ||
| 510 | static const char conv_str[] ALIGN1 = | 515 | static 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) | |||
| 571 | static NOINLINE void display(priv_dumper_t* dumper) | 578 | static 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 @@ | |||
| 1 | ELIF1 | ||
| 2 | ELIF2 | ||
| 3 | ELIF THEN | ||
| 4 | Ok: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 @@ | |||
| 1 | if false; then | ||
| 2 | : | ||
| 3 | elif echo 'ELIF1'; echo 'ELIF2'; then | ||
| 4 | echo "ELIF THEN" | ||
| 5 | fi | ||
| 6 | echo "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 @@ | |||
| 1 | THEN | ||
| 2 | Ok: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 @@ | |||
| 1 | if true; then | ||
| 2 | echo "THEN" | ||
| 3 | elif echo "ELIF false"; false; then | ||
| 4 | echo "ELIF THEN" | ||
| 5 | else | ||
| 6 | echo "ELSE" | ||
| 7 | fi | ||
| 8 | echo "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 |
| 1062 | static int builtin_readonly(char **argv) FAST_FUNC; | 1062 | static int builtin_readonly(char **argv) FAST_FUNC; |
| 1063 | #endif | 1063 | #endif |
| 1064 | static int builtin_false(char **argv) FAST_FUNC; | ||
| 1064 | #if ENABLE_HUSH_JOB | 1065 | #if ENABLE_HUSH_JOB |
| 1065 | static int builtin_fg_bg(char **argv) FAST_FUNC; | 1066 | static int builtin_fg_bg(char **argv) FAST_FUNC; |
| 1066 | static int builtin_jobs(char **argv) FAST_FUNC; | 1067 | static 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 | ||
| 10836 | static 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 |
| 10827 | static NOINLINE int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv)) | 10842 | static 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 @@ | |||
| 1 | ELIF1 | ||
| 2 | ELIF2 | ||
| 3 | ELIF THEN | ||
| 4 | Ok: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 @@ | |||
| 1 | if false; then | ||
| 2 | : | ||
| 3 | elif echo 'ELIF1'; echo 'ELIF2'; then | ||
| 4 | echo "ELIF THEN" | ||
| 5 | fi | ||
| 6 | echo "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 @@ | |||
| 1 | THEN | ||
| 2 | Ok: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 @@ | |||
| 1 | if true; then | ||
| 2 | echo "THEN" | ||
| 3 | elif echo "ELIF false"; false; then | ||
| 4 | echo "ELIF THEN" | ||
| 5 | else | ||
| 6 | echo "ELSE" | ||
| 7 | fi | ||
| 8 | echo "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' |
| 500 | SKIP= | 500 | SKIP= |
| 501 | 501 | ||
| 502 | # User-supplied bug (SEGV) example, was causing use-after-realloc | ||
| 503 | testing '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 | ||
| 529 | 0 | ||
| 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. | ||
| 559 | testing '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 | |||
| 564 | testing "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 | |||
| 569 | sq="'" | ||
| 570 | testing 'awk gensub backslashes \' \ | ||
| 571 | 'awk '$sq'BEGIN { s="\\"; print "s=" s; print gensub("a", s, "g", "a|a") }'$sq \ | ||
| 572 | 's=\\ | ||
| 573 | \\|\\ | ||
| 574 | ' '' '' | ||
| 575 | testing '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. | ||
| 583 | testing 'awk gensub backslashes \\\' \ | ||
| 584 | 'awk '$sq'BEGIN { s="\\\\\\"; print "s=" s; print gensub("a", s, "g", "a|a") }'$sq \ | ||
| 585 | 's=\\\\\\ | ||
| 586 | \\\\|\\\\ | ||
| 587 | ' '' '' | ||
| 588 | testing 'awk gensub backslashes \\\\' \ | ||
| 589 | 'awk '$sq'BEGIN { s="\\\\\\\\"; print "s=" s; print gensub("a", s, "g", "a|a") }'$sq \ | ||
| 590 | 's=\\\\\\\\ | ||
| 591 | \\\\|\\\\ | ||
| 592 | ' '' '' | ||
| 593 | testing 'awk gensub backslashes \&' \ | ||
| 594 | 'awk '$sq'BEGIN { s="\\&"; print "s=" s; print gensub("a", s, "g", "a|a") }'$sq \ | ||
| 595 | 's=\\& | ||
| 596 | &|& | ||
| 597 | ' '' '' | ||
| 598 | testing 'awk gensub backslashes \0' \ | ||
| 599 | 'awk '$sq'BEGIN { s="\\0"; print "s=" s; print gensub("a", s, "g", "a|a") }'$sq \ | ||
| 600 | 's=\\0 | ||
| 601 | a|a | ||
| 602 | ' '' '' | ||
| 603 | testing '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. | ||
| 613 | testing 'awk gsub erroneous word start match' \ | ||
| 614 | "awk 'BEGIN { a=\"abc\"; gsub(/\<b*/,\"\",a); print a }'" \ | ||
| 615 | 'abc\n' \ | ||
| 616 | '' '' | ||
| 617 | |||
| 502 | exit $FAILCOUNT | 618 | exit $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 | ||
| 37 | testing "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 | |||
| 53 | testing "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 | |||
| 69 | testing "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 | |||
| 37 | exit $FAILCOUNT | 85 | exit $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 | ||
| 9 | input="$(printf '\001\002\003\nABC\xfe')" | ||
| 10 | |||
| 11 | little_endian=false | ||
| 12 | { printf '\0\1' | od -s | grep -q 256; } && little_endian=true | ||
| 13 | readonly little_endian | ||
| 14 | |||
| 15 | $little_endian || SKIP=1 | ||
| 16 | testing "od (little-endian)" \ | ||
| 17 | "od" \ | ||
| 18 | "\ | ||
| 19 | 0000000 001001 005003 041101 177103 | ||
| 20 | 0000010 | ||
| 21 | " \ | ||
| 22 | "" "$input" | ||
| 23 | SKIP= | ||
| 24 | |||
| 25 | optional !DESKTOP | ||
| 26 | testing "od -a (!DESKTOP)" \ | ||
| 27 | "od -a" \ | ||
| 28 | "\ | ||
| 29 | 0000000 nul soh stx etx eot enq ack bel bs ht lf vt ff cr so si | ||
| 30 | 0000020 dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us | ||
| 31 | 0000040 p q r s t u v w x y z { | } ~ del | ||
| 32 | 0000060 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f | ||
| 33 | 0000100 f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff | ||
| 34 | 0000120 | ||
| 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" | ||
| 42 | SKIP= | ||
| 43 | # ^^^ a bit incorrect handling of ctrl ("lf" should be "nl") and high bytes. | ||
| 44 | # vvv this output is correct. | ||
| 45 | optional DESKTOP | ||
| 46 | testing "od -a (DESKTOP)" \ | ||
| 47 | "od -a" \ | ||
| 48 | "\ | ||
| 49 | 0000000 nul soh stx etx eot enq ack bel bs ht nl vt ff cr so si | ||
| 50 | 0000020 dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us | ||
| 51 | 0000040 p q r s t u v w x y z { | } ~ del | ||
| 52 | 0000060 nul soh stx etx eot enq ack bel bs ht nl vt ff cr so si | ||
| 53 | 0000100 p q r s t u v w x y z { | } ~ del | ||
| 54 | 0000120 | ||
| 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" | ||
| 62 | SKIP= | ||
| 63 | |||
| 64 | testing "od -B" \ | ||
| 65 | "od -B" \ | ||
| 66 | "\ | ||
| 67 | 0000000 001001 005003 041101 177103 | ||
| 68 | 0000010 | ||
| 69 | " \ | ||
| 70 | "" "$input" | ||
| 71 | SKIP= | ||
| 72 | |||
| 73 | $little_endian || SKIP=1 | ||
| 74 | testing "od -o (little-endian)" \ | ||
| 75 | "od -o" \ | ||
| 76 | "\ | ||
| 77 | 0000000 001001 005003 041101 177103 | ||
| 78 | 0000010 | ||
| 79 | " \ | ||
| 80 | "" "$input" | ||
| 81 | SKIP= | ||
| 82 | |||
| 83 | testing "od -b" \ | ||
| 84 | "od -b" \ | ||
| 85 | "\ | ||
| 86 | 0000000 001 002 003 012 101 102 103 376 | ||
| 87 | 0000010 | ||
| 88 | " \ | ||
| 89 | "" "$input" | ||
| 90 | SKIP= | ||
| 91 | |||
| 92 | testing "od -c" \ | ||
| 93 | "od -c" \ | ||
| 94 | "\ | ||
| 95 | 0000000 001 002 003 \\\\n A B C 376 | ||
| 96 | 0000010 | ||
| 97 | " \ | ||
| 98 | "" "$input" | ||
| 99 | SKIP= | ||
| 100 | |||
| 101 | $little_endian || SKIP=1 | ||
| 102 | testing "od -d (little-endian)" \ | ||
| 103 | "od -d" \ | ||
| 104 | "\ | ||
| 105 | 0000000 513 2563 16961 65091 | ||
| 106 | 0000010 | ||
| 107 | " \ | ||
| 108 | "" "$input" | ||
| 109 | SKIP= | ||
| 110 | |||
| 111 | $little_endian || SKIP=1 | ||
| 112 | testing "od -D (little-endian)" \ | ||
| 113 | "od -D" \ | ||
| 114 | "\ | ||
| 115 | 0000000 167969281 4265820737 | ||
| 116 | 0000010 | ||
| 117 | " \ | ||
| 118 | "" "$input" | ||
| 119 | SKIP= | ||
| 120 | |||
| 121 | optional !DESKTOP #DESKTOP: unrecognized option: e | ||
| 122 | $little_endian || SKIP=1 | ||
| 123 | testing "od -e (!DESKTOP little-endian)" \ | ||
| 124 | "od -e" \ | ||
| 125 | "\ | ||
| 126 | 0000000 -1.61218556514036e+300 | ||
| 127 | 0000010 | ||
| 128 | " \ | ||
| 129 | "" "$input" | ||
| 130 | SKIP= | ||
| 131 | |||
| 132 | optional !DESKTOP #DESKTOP: unrecognized option: F | ||
| 133 | $little_endian || SKIP=1 | ||
| 134 | testing "od -F (!DESKTOP little-endian)" \ | ||
| 135 | "od -F" \ | ||
| 136 | "\ | ||
| 137 | 0000000 -1.61218556514036e+300 | ||
| 138 | 0000010 | ||
| 139 | " \ | ||
| 140 | "" "$input" | ||
| 141 | SKIP= | ||
| 142 | |||
| 143 | $little_endian || SKIP=1 | ||
| 144 | testing "od -f (little-endian)" \ | ||
| 145 | "od -f" \ | ||
| 146 | "\ | ||
| 147 | 0000000 6.3077975e-33 -6.4885867e+37 | ||
| 148 | 0000010 | ||
| 149 | " \ | ||
| 150 | "" "$input" | ||
| 151 | SKIP= | ||
| 152 | |||
| 153 | $little_endian || SKIP=1 | ||
| 154 | testing "od -H (little-endian)" \ | ||
| 155 | "od -H" \ | ||
| 156 | "\ | ||
| 157 | 0000000 0a030201 fe434241 | ||
| 158 | 0000010 | ||
| 159 | " \ | ||
| 160 | "" "$input" | ||
| 161 | SKIP= | ||
| 162 | |||
| 163 | $little_endian || SKIP=1 | ||
| 164 | testing "od -X (little-endian)" \ | ||
| 165 | "od -X" \ | ||
| 166 | "\ | ||
| 167 | 0000000 0a030201 fe434241 | ||
| 168 | 0000010 | ||
| 169 | " \ | ||
| 170 | "" "$input" | ||
| 171 | SKIP= | ||
| 172 | |||
| 173 | $little_endian || SKIP=1 | ||
| 174 | testing "od -h (little-endian)" \ | ||
| 175 | "od -h" \ | ||
| 176 | "\ | ||
| 177 | 0000000 0201 0a03 4241 fe43 | ||
| 178 | 0000010 | ||
| 179 | " \ | ||
| 180 | "" "$input" | ||
| 181 | SKIP= | ||
| 182 | |||
| 183 | $little_endian || SKIP=1 | ||
| 184 | testing "od -x (little-endian)" \ | ||
| 185 | "od -x" \ | ||
| 186 | "\ | ||
| 187 | 0000000 0201 0a03 4241 fe43 | ||
| 188 | 0000010 | ||
| 189 | " \ | ||
| 190 | "" "$input" | ||
| 191 | SKIP= | ||
| 192 | |||
| 193 | $little_endian || SKIP=1 | ||
| 194 | testing "od -i (little-endian)" \ | ||
| 195 | "od -i" \ | ||
| 196 | "\ | ||
| 197 | 0000000 167969281 -29146559 | ||
| 198 | 0000010 | ||
| 199 | " \ | ||
| 200 | "" "$input" | ||
| 201 | SKIP= | ||
| 202 | |||
| 203 | $little_endian || SKIP=1 | ||
| 204 | testing "od -O (little-endian)" \ | ||
| 205 | "od -O" \ | ||
| 206 | "\ | ||
| 207 | 0000000 01200601001 37620641101 | ||
| 208 | 0000010 | ||
| 209 | " \ | ||
| 210 | "" "$input" | ||
| 211 | SKIP= | ||
| 212 | |||
| 213 | # 32-bit? | ||
| 214 | printf '00000000' | od -l | grep -q '808464432 *808464432' && SKIP=1 #yes, skip | ||
| 215 | $little_endian || SKIP=1 | ||
| 216 | testing "od -I (little-endian)" \ | ||
| 217 | "od -I" \ | ||
| 218 | "\ | ||
| 219 | 0000000 -125183517527965183 | ||
| 220 | 0000010 | ||
| 221 | " \ | ||
| 222 | "" "$input" | ||
| 223 | testing "od -L (little-endian)" \ | ||
| 224 | "od -L" \ | ||
| 225 | "\ | ||
| 226 | 0000000 -125183517527965183 | ||
| 227 | 0000010 | ||
| 228 | " \ | ||
| 229 | "" "$input" | ||
| 230 | testing "od -l (little-endian)" \ | ||
| 231 | "od -l" \ | ||
| 232 | "\ | ||
| 233 | 0000000 -125183517527965183 | ||
| 234 | 0000010 | ||
| 235 | " \ | ||
| 236 | "" "$input" | ||
| 237 | SKIP= | ||
| 238 | |||
| 9 | optional DESKTOP | 239 | optional DESKTOP |
| 10 | testing "od -b" \ | 240 | testing "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 | ||
| 74 | static const char *const add_strings[] ALIGN_PTR = { | 74 | static 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 | ||
| 82 | static const char add_first[] ALIGN1 = "\"%07.7_Ax\n\""; | 82 | static 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 | ||
| 84 | static const char hexdump_opts[] ALIGN1 = "bcdoxCe:f:n:s:v"; | 90 | static 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) { |
