diff options
author | Ron Yorston <rmy@pobox.com> | 2015-05-18 09:36:27 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2015-05-18 09:36:27 +0100 |
commit | 60063627a6d540871061854a362047e6517f821c (patch) | |
tree | 0de228630450c64e085f2e3f5141b5ba17eccab3 | |
parent | ec39cb770ddd5c0e085d5c4ee10be65bab5e7a44 (diff) | |
parent | 9a595bb36ded308e6d4336aef2c1cd3ac738a398 (diff) | |
download | busybox-w32-60063627a6d540871061854a362047e6517f821c.tar.gz busybox-w32-60063627a6d540871061854a362047e6517f821c.tar.bz2 busybox-w32-60063627a6d540871061854a362047e6517f821c.zip |
Merge branch 'busybox' into mergeFRP
80 files changed, 832 insertions, 411 deletions
diff --git a/Makefile.flags b/Makefile.flags index d9122ce94..a2fba5028 100644 --- a/Makefile.flags +++ b/Makefile.flags | |||
@@ -170,7 +170,7 @@ SKIP_STRIP = y | |||
170 | endif | 170 | endif |
171 | 171 | ||
172 | ifneq ($(CONFIG_EXTRA_LDFLAGS),) | 172 | ifneq ($(CONFIG_EXTRA_LDFLAGS),) |
173 | EXTRA_LDFLAGS += $(strip $(subst ",,$(CONFIG_EXTRA_LDFLAGS))) | 173 | LDFLAGS += $(strip $(subst ",,$(CONFIG_EXTRA_LDFLAGS))) |
174 | #")) | 174 | #")) |
175 | endif | 175 | endif |
176 | 176 | ||
@@ -186,7 +186,7 @@ Supported hardware: | |||
186 | Under 2.4 Linux kernels, kernel module loading was implemented in a | 186 | Under 2.4 Linux kernels, kernel module loading was implemented in a |
187 | platform-specific manner. Busybox's insmod utility has been reported to | 187 | platform-specific manner. Busybox's insmod utility has been reported to |
188 | work under ARM, CRIS, H8/300, x86, ia64, x86_64, m68k, MIPS, PowerPC, S390, | 188 | work under ARM, CRIS, H8/300, x86, ia64, x86_64, m68k, MIPS, PowerPC, S390, |
189 | SH3/4/5, Sparc, v850e, and x86_64. Anything else probably won't work. | 189 | SH3/4/5, Sparc, and v850e. Anything else probably won't work. |
190 | 190 | ||
191 | The module loading mechanism for the 2.6 kernel is much more generic, and | 191 | The module loading mechanism for the 2.6 kernel is much more generic, and |
192 | we believe 2.6.x kernel module loading support should work on all | 192 | we believe 2.6.x kernel module loading support should work on all |
diff --git a/archival/gzip.c b/archival/gzip.c index bc1f9c60b..42b2f0b2e 100644 --- a/archival/gzip.c +++ b/archival/gzip.c | |||
@@ -62,14 +62,27 @@ aa: 85.1% -- replaced with aa.gz | |||
62 | //config: 1: larger buffers, larger hash-tables | 62 | //config: 1: larger buffers, larger hash-tables |
63 | //config: 2: larger buffers, largest hash-tables | 63 | //config: 2: larger buffers, largest hash-tables |
64 | //config: Larger models may give slightly better compression | 64 | //config: Larger models may give slightly better compression |
65 | //config: | ||
66 | //config:config FEATURE_GZIP_LEVELS | ||
67 | //config: bool "Enable compression levels" | ||
68 | //config: default n | ||
69 | //config: depends on GZIP | ||
70 | //config: help | ||
71 | //config: Enable support for compression levels 4-9. The default level | ||
72 | //config: is 6. If levels 1-3 are specified, 4 is used. | ||
73 | //config: If this option is not selected, -N options are ignored and -9 | ||
74 | //config: is used. | ||
65 | 75 | ||
66 | //applet:IF_GZIP(APPLET(gzip, BB_DIR_BIN, BB_SUID_DROP)) | 76 | //applet:IF_GZIP(APPLET(gzip, BB_DIR_BIN, BB_SUID_DROP)) |
67 | //kbuild:lib-$(CONFIG_GZIP) += gzip.o | 77 | //kbuild:lib-$(CONFIG_GZIP) += gzip.o |
68 | 78 | ||
69 | //usage:#define gzip_trivial_usage | 79 | //usage:#define gzip_trivial_usage |
70 | //usage: "[-cfd] [FILE]..." | 80 | //usage: "[-cfd" IF_FEATURE_GZIP_LEVELS("123456789") "] [FILE]..." |
71 | //usage:#define gzip_full_usage "\n\n" | 81 | //usage:#define gzip_full_usage "\n\n" |
72 | //usage: "Compress FILEs (or stdin)\n" | 82 | //usage: "Compress FILEs (or stdin)\n" |
83 | //usage: IF_FEATURE_GZIP_LEVELS( | ||
84 | //usage: "\n -1..9 Compression level" | ||
85 | //usage: ) | ||
73 | //usage: "\n -d Decompress" | 86 | //usage: "\n -d Decompress" |
74 | //usage: "\n -c Write to stdout" | 87 | //usage: "\n -c Write to stdout" |
75 | //usage: "\n -f Force" | 88 | //usage: "\n -f Force" |
@@ -252,6 +265,8 @@ enum { | |||
252 | * input file length plus MIN_LOOKAHEAD. | 265 | * input file length plus MIN_LOOKAHEAD. |
253 | */ | 266 | */ |
254 | 267 | ||
268 | #ifndef ENABLE_FEATURE_GZIP_LEVELS | ||
269 | |||
255 | max_chain_length = 4096, | 270 | max_chain_length = 4096, |
256 | /* To speed up deflation, hash chains are never searched beyond this length. | 271 | /* To speed up deflation, hash chains are never searched beyond this length. |
257 | * A higher limit improves compression ratio but degrades the speed. | 272 | * A higher limit improves compression ratio but degrades the speed. |
@@ -283,11 +298,23 @@ enum { | |||
283 | * For deflate_fast() (levels <= 3) good is ignored and lazy has a different | 298 | * For deflate_fast() (levels <= 3) good is ignored and lazy has a different |
284 | * meaning. | 299 | * meaning. |
285 | */ | 300 | */ |
301 | #endif /* ENABLE_FEATURE_GZIP_LEVELS */ | ||
286 | }; | 302 | }; |
287 | 303 | ||
288 | 304 | ||
289 | struct globals { | 305 | struct globals { |
290 | 306 | ||
307 | #ifdef ENABLE_FEATURE_GZIP_LEVELS | ||
308 | unsigned max_chain_length; | ||
309 | unsigned max_lazy_match; | ||
310 | unsigned good_match; | ||
311 | unsigned nice_match; | ||
312 | #define max_chain_length (G1.max_chain_length) | ||
313 | #define max_lazy_match (G1.max_lazy_match) | ||
314 | #define good_match (G1.good_match) | ||
315 | #define nice_match (G1.nice_match) | ||
316 | #endif | ||
317 | |||
291 | lng block_start; | 318 | lng block_start; |
292 | 319 | ||
293 | /* window position at the beginning of the current output block. Gets | 320 | /* window position at the beginning of the current output block. Gets |
@@ -2161,24 +2188,48 @@ int gzip_main(int argc UNUSED_PARAM, char **argv) | |||
2161 | #endif | 2188 | #endif |
2162 | { | 2189 | { |
2163 | unsigned opt; | 2190 | unsigned opt; |
2191 | #ifdef ENABLE_FEATURE_GZIP_LEVELS | ||
2192 | static const struct { | ||
2193 | uint8_t good; | ||
2194 | uint8_t chain_shift; | ||
2195 | uint8_t lazy2; | ||
2196 | uint8_t nice2; | ||
2197 | } gzip_level_config[6] = { | ||
2198 | {4, 4, 4/2, 16/2}, /* Level 4 */ | ||
2199 | {8, 5, 16/2, 32/2}, /* Level 5 */ | ||
2200 | {8, 7, 16/2, 128/2}, /* Level 6 */ | ||
2201 | {8, 8, 32/2, 128/2}, /* Level 7 */ | ||
2202 | {32, 10, 128/2, 258/2}, /* Level 8 */ | ||
2203 | {32, 12, 258/2, 258/2}, /* Level 9 */ | ||
2204 | }; | ||
2205 | #endif | ||
2206 | |||
2207 | SET_PTR_TO_GLOBALS((char *)xzalloc(sizeof(struct globals)+sizeof(struct globals2)) | ||
2208 | + sizeof(struct globals)); | ||
2164 | 2209 | ||
2165 | #if ENABLE_FEATURE_GZIP_LONG_OPTIONS | 2210 | #if ENABLE_FEATURE_GZIP_LONG_OPTIONS |
2166 | applet_long_options = gzip_longopts; | 2211 | applet_long_options = gzip_longopts; |
2167 | #endif | 2212 | #endif |
2168 | /* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */ | 2213 | /* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */ |
2169 | opt = getopt32(argv, "cfv" IF_GUNZIP("dt") "q123456789n"); | 2214 | opt = getopt32(argv, "cfv" IF_GUNZIP("dt") "qn123456789"); |
2170 | #if ENABLE_GUNZIP /* gunzip_main may not be visible... */ | 2215 | #if ENABLE_GUNZIP /* gunzip_main may not be visible... */ |
2171 | if (opt & 0x18) // -d and/or -t | 2216 | if (opt & 0x18) // -d and/or -t |
2172 | return gunzip_main(argc, argv); | 2217 | return gunzip_main(argc, argv); |
2173 | #endif | 2218 | #endif |
2174 | option_mask32 &= 0x7; /* ignore -q, -0..9 */ | 2219 | #ifdef ENABLE_FEATURE_GZIP_LEVELS |
2175 | //if (opt & 0x1) // -c | 2220 | opt >>= ENABLE_GUNZIP ? 7 : 5; /* drop cfv[dt]qn bits */ |
2176 | //if (opt & 0x2) // -f | 2221 | if (opt == 0) |
2177 | //if (opt & 0x4) // -v | 2222 | opt = 1 << 6; /* default: 6 */ |
2178 | argv += optind; | 2223 | /* Map 1..3 to 4 */ |
2179 | 2224 | if (opt & 0x7) | |
2180 | SET_PTR_TO_GLOBALS((char *)xzalloc(sizeof(struct globals)+sizeof(struct globals2)) | 2225 | opt |= 1 << 4; |
2181 | + sizeof(struct globals)); | 2226 | opt = ffs(opt >> 3); |
2227 | max_chain_length = 1 << gzip_level_config[opt].chain_shift; | ||
2228 | good_match = gzip_level_config[opt].good; | ||
2229 | max_lazy_match = gzip_level_config[opt].lazy2 * 2; | ||
2230 | nice_match = gzip_level_config[opt].nice2 * 2; | ||
2231 | #endif | ||
2232 | option_mask32 &= 0x7; /* retain only -cfv */ | ||
2182 | 2233 | ||
2183 | /* Allocate all global buffers (for DYN_ALLOC option) */ | 2234 | /* Allocate all global buffers (for DYN_ALLOC option) */ |
2184 | ALLOC(uch, G1.l_buf, INBUFSIZ); | 2235 | ALLOC(uch, G1.l_buf, INBUFSIZ); |
@@ -2190,5 +2241,6 @@ int gzip_main(int argc UNUSED_PARAM, char **argv) | |||
2190 | /* Initialize the CRC32 table */ | 2241 | /* Initialize the CRC32 table */ |
2191 | global_crc32_table = crc32_filltable(NULL, 0); | 2242 | global_crc32_table = crc32_filltable(NULL, 0); |
2192 | 2243 | ||
2244 | argv += optind; | ||
2193 | return bbunpack(argv, pack_gzip, append_ext, "gz"); | 2245 | return bbunpack(argv, pack_gzip, append_ext, "gz"); |
2194 | } | 2246 | } |
diff --git a/archival/libarchive/get_header_tar.c b/archival/libarchive/get_header_tar.c index 2dbcdb50c..fb68673b9 100644 --- a/archival/libarchive/get_header_tar.c +++ b/archival/libarchive/get_header_tar.c | |||
@@ -350,7 +350,14 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) | |||
350 | case '6': | 350 | case '6': |
351 | file_header->mode |= S_IFIFO; | 351 | file_header->mode |= S_IFIFO; |
352 | goto size0; | 352 | goto size0; |
353 | case 'g': /* pax global header */ | ||
354 | case 'x': { /* pax extended header */ | ||
355 | if ((uoff_t)file_header->size > 0xfffff) /* paranoia */ | ||
356 | goto skip_ext_hdr; | ||
357 | process_pax_hdr(archive_handle, file_header->size, (tar.typeflag == 'g')); | ||
358 | goto again_after_align; | ||
353 | #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS | 359 | #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS |
360 | /* See http://www.gnu.org/software/tar/manual/html_node/Extensions.html */ | ||
354 | case 'L': | 361 | case 'L': |
355 | /* free: paranoia: tar with several consecutive longnames */ | 362 | /* free: paranoia: tar with several consecutive longnames */ |
356 | free(p_longname); | 363 | free(p_longname); |
@@ -370,18 +377,17 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) | |||
370 | archive_handle->offset += file_header->size; | 377 | archive_handle->offset += file_header->size; |
371 | /* return get_header_tar(archive_handle); */ | 378 | /* return get_header_tar(archive_handle); */ |
372 | goto again; | 379 | goto again; |
373 | case 'D': /* GNU dump dir */ | 380 | /* |
374 | case 'M': /* Continuation of multi volume archive */ | 381 | * case 'S': // Sparse file |
375 | case 'N': /* Old GNU for names > 100 characters */ | 382 | * Was seen in the wild. Not supported (yet?). |
376 | case 'S': /* Sparse file */ | 383 | * See https://www.gnu.org/software/tar/manual/html_section/tar_92.html |
377 | case 'V': /* Volume header */ | 384 | * for the format. (An "Old GNU Format" was seen, not PAX formats). |
385 | */ | ||
386 | // case 'D': /* GNU dump dir */ | ||
387 | // case 'M': /* Continuation of multi volume archive */ | ||
388 | // case 'N': /* Old GNU for names > 100 characters */ | ||
389 | // case 'V': /* Volume header */ | ||
378 | #endif | 390 | #endif |
379 | case 'g': /* pax global header */ | ||
380 | case 'x': { /* pax extended header */ | ||
381 | if ((uoff_t)file_header->size > 0xfffff) /* paranoia */ | ||
382 | goto skip_ext_hdr; | ||
383 | process_pax_hdr(archive_handle, file_header->size, (tar.typeflag == 'g')); | ||
384 | goto again_after_align; | ||
385 | } | 391 | } |
386 | skip_ext_hdr: | 392 | skip_ext_hdr: |
387 | { | 393 | { |
diff --git a/archival/libarchive/open_transformer.c b/archival/libarchive/open_transformer.c index 0a8c657b7..1c5c185d0 100644 --- a/archival/libarchive/open_transformer.c +++ b/archival/libarchive/open_transformer.c | |||
@@ -183,6 +183,13 @@ static transformer_state_t *setup_transformer_on_fd(int fd, int fail_if_not_comp | |||
183 | USE_FOR_NOMMU(xstate->xformer_prog = "gunzip";) | 183 | USE_FOR_NOMMU(xstate->xformer_prog = "gunzip";) |
184 | goto found_magic; | 184 | goto found_magic; |
185 | } | 185 | } |
186 | if (ENABLE_FEATURE_SEAMLESS_Z | ||
187 | && magic.b16[0] == COMPRESS_MAGIC | ||
188 | ) { | ||
189 | xstate->xformer = unpack_Z_stream; | ||
190 | USE_FOR_NOMMU(xstate->xformer_prog = "uncompress";) | ||
191 | goto found_magic; | ||
192 | } | ||
186 | if (ENABLE_FEATURE_SEAMLESS_BZ2 | 193 | if (ENABLE_FEATURE_SEAMLESS_BZ2 |
187 | && magic.b16[0] == BZIP2_MAGIC | 194 | && magic.b16[0] == BZIP2_MAGIC |
188 | ) { | 195 | ) { |
diff --git a/coreutils/false.c b/coreutils/false.c index 59c2f321a..0591a6cdc 100644 --- a/coreutils/false.c +++ b/coreutils/false.c | |||
@@ -10,11 +10,9 @@ | |||
10 | /* BB_AUDIT SUSv3 compliant */ | 10 | /* BB_AUDIT SUSv3 compliant */ |
11 | /* http://www.opengroup.org/onlinepubs/000095399/utilities/false.html */ | 11 | /* http://www.opengroup.org/onlinepubs/000095399/utilities/false.html */ |
12 | 12 | ||
13 | //usage:#define false_trivial_usage | 13 | /* "false --help" is special-cased to ignore --help */ |
14 | //usage: "" | 14 | //usage:#define false_trivial_usage NOUSAGE_STR |
15 | //usage:#define false_full_usage "\n\n" | 15 | //usage:#define false_full_usage "" |
16 | //usage: "Return an exit code of FALSE (1)" | ||
17 | //usage: | ||
18 | //usage:#define false_example_usage | 16 | //usage:#define false_example_usage |
19 | //usage: "$ false\n" | 17 | //usage: "$ false\n" |
20 | //usage: "$ echo $?\n" | 18 | //usage: "$ echo $?\n" |
diff --git a/coreutils/test.c b/coreutils/test.c index 9e709a709..422d24c54 100644 --- a/coreutils/test.c +++ b/coreutils/test.c | |||
@@ -39,14 +39,9 @@ | |||
39 | //config: help | 39 | //config: help |
40 | //config: Enable 64-bit support in test. | 40 | //config: Enable 64-bit support in test. |
41 | 41 | ||
42 | /* "test --help" does not print help (POSIX compat), only "[ --help" does. | 42 | /* "test --help" is special-cased to ignore --help */ |
43 | * We display "<applet> EXPRESSION ]" here (not "<applet> EXPRESSION") | 43 | //usage:#define test_trivial_usage NOUSAGE_STR |
44 | * Unfortunately, it screws up generated BusyBox.html. TODO. */ | 44 | //usage:#define test_full_usage "" |
45 | //usage:#define test_trivial_usage | ||
46 | //usage: "EXPRESSION ]" | ||
47 | //usage:#define test_full_usage "\n\n" | ||
48 | //usage: "Check file types, compare values etc. Return a 0/1 exit code\n" | ||
49 | //usage: "depending on logical value of EXPRESSION" | ||
50 | //usage: | 45 | //usage: |
51 | //usage:#define test_example_usage | 46 | //usage:#define test_example_usage |
52 | //usage: "$ test 1 -eq 2\n" | 47 | //usage: "$ test 1 -eq 2\n" |
diff --git a/coreutils/true.c b/coreutils/true.c index 382e476a8..89f892961 100644 --- a/coreutils/true.c +++ b/coreutils/true.c | |||
@@ -10,11 +10,9 @@ | |||
10 | /* BB_AUDIT SUSv3 compliant */ | 10 | /* BB_AUDIT SUSv3 compliant */ |
11 | /* http://www.opengroup.org/onlinepubs/007904975/utilities/true.html */ | 11 | /* http://www.opengroup.org/onlinepubs/007904975/utilities/true.html */ |
12 | 12 | ||
13 | //usage:#define true_trivial_usage | 13 | /* "true --help" is special-cased to ignore --help */ |
14 | //usage: "" | 14 | //usage:#define true_trivial_usage NOUSAGE_STR |
15 | //usage:#define true_full_usage "\n\n" | 15 | //usage:#define true_full_usage "" |
16 | //usage: "Return an exit code of TRUE (0)" | ||
17 | //usage: | ||
18 | //usage:#define true_example_usage | 16 | //usage:#define true_example_usage |
19 | //usage: "$ true\n" | 17 | //usage: "$ true\n" |
20 | //usage: "$ echo $?\n" | 18 | //usage: "$ echo $?\n" |
diff --git a/coreutils/who.c b/coreutils/who.c index f955ce6d3..8337212c9 100644 --- a/coreutils/who.c +++ b/coreutils/who.c | |||
@@ -73,7 +73,7 @@ static void idle_string(char *str6, time_t t) | |||
73 | int who_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 73 | int who_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
74 | int who_main(int argc UNUSED_PARAM, char **argv) | 74 | int who_main(int argc UNUSED_PARAM, char **argv) |
75 | { | 75 | { |
76 | struct utmp *ut; | 76 | struct utmpx *ut; |
77 | unsigned opt; | 77 | unsigned opt; |
78 | int do_users = (ENABLE_USERS && (!ENABLE_WHO || applet_name[0] == 'u')); | 78 | int do_users = (ENABLE_USERS && (!ENABLE_WHO || applet_name[0] == 'u')); |
79 | const char *fmt = "%s"; | 79 | const char *fmt = "%s"; |
@@ -83,8 +83,8 @@ int who_main(int argc UNUSED_PARAM, char **argv) | |||
83 | if (opt & 2) // -H | 83 | if (opt & 2) // -H |
84 | printf("USER\t\tTTY\t\tIDLE\tTIME\t\t HOST\n"); | 84 | printf("USER\t\tTTY\t\tIDLE\tTIME\t\t HOST\n"); |
85 | 85 | ||
86 | setutent(); | 86 | setutxent(); |
87 | while ((ut = getutent()) != NULL) { | 87 | while ((ut = getutxent()) != NULL) { |
88 | if (ut->ut_user[0] | 88 | if (ut->ut_user[0] |
89 | && ((opt & 1) || ut->ut_type == USER_PROCESS) | 89 | && ((opt & 1) || ut->ut_type == USER_PROCESS) |
90 | ) { | 90 | ) { |
@@ -126,6 +126,6 @@ int who_main(int argc UNUSED_PARAM, char **argv) | |||
126 | if (do_users) | 126 | if (do_users) |
127 | bb_putchar('\n'); | 127 | bb_putchar('\n'); |
128 | if (ENABLE_FEATURE_CLEAN_UP) | 128 | if (ENABLE_FEATURE_CLEAN_UP) |
129 | endutent(); | 129 | endutxent(); |
130 | return EXIT_SUCCESS; | 130 | return EXIT_SUCCESS; |
131 | } | 131 | } |
diff --git a/e2fsprogs/old_e2fsprogs/e2fsck.c b/e2fsprogs/old_e2fsprogs/e2fsck.c index 8400a92ce..0799b64ad 100644 --- a/e2fsprogs/old_e2fsprogs/e2fsck.c +++ b/e2fsprogs/old_e2fsprogs/e2fsck.c | |||
@@ -11462,7 +11462,7 @@ static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, | |||
11462 | count = 1; | 11462 | count = 1; |
11463 | } | 11463 | } |
11464 | if (retval) { | 11464 | if (retval) { |
11465 | bb_error_msg(_("while calling ext2fs_adjust_ea_refocunt for inode %d"), | 11465 | bb_error_msg(_("while calling ext2fs_adjust_ea_refcount for inode %d"), |
11466 | ino); | 11466 | ino); |
11467 | return 1; | 11467 | return 1; |
11468 | } | 11468 | } |
diff --git a/editors/sed.c b/editors/sed.c index 2c64ad500..7bbf820d8 100644 --- a/editors/sed.c +++ b/editors/sed.c | |||
@@ -53,6 +53,7 @@ | |||
53 | * Reference | 53 | * Reference |
54 | * http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html | 54 | * http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html |
55 | * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html | 55 | * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html |
56 | * http://sed.sourceforge.net/sedfaq3.html | ||
56 | */ | 57 | */ |
57 | 58 | ||
58 | //config:config SED | 59 | //config:config SED |
@@ -109,7 +110,8 @@ typedef struct sed_cmd_s { | |||
109 | regex_t *sub_match; /* For 's/sub_match/string/' */ | 110 | regex_t *sub_match; /* For 's/sub_match/string/' */ |
110 | int beg_line; /* 'sed 1p' 0 == apply commands to all lines */ | 111 | int beg_line; /* 'sed 1p' 0 == apply commands to all lines */ |
111 | int beg_line_orig; /* copy of the above, needed for -i */ | 112 | int beg_line_orig; /* copy of the above, needed for -i */ |
112 | int end_line; /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */ | 113 | int end_line; /* 'sed 1,3p' 0 == one line only. -1 = last line ($). -2-N = +N */ |
114 | int end_line_orig; | ||
113 | 115 | ||
114 | FILE *sw_file; /* File (sw) command writes to, -1 for none. */ | 116 | FILE *sw_file; /* File (sw) command writes to, -1 for none. */ |
115 | char *string; /* Data string for (saicytb) commands. */ | 117 | char *string; /* Data string for (saicytb) commands. */ |
@@ -640,10 +642,29 @@ static void add_cmd(const char *cmdstr) | |||
640 | int idx; | 642 | int idx; |
641 | 643 | ||
642 | cmdstr++; | 644 | cmdstr++; |
643 | idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match); | 645 | if (*cmdstr == '+' && isdigit(cmdstr[1])) { |
644 | if (!idx) | 646 | /* http://sed.sourceforge.net/sedfaq3.html#s3.3 |
647 | * Under GNU sed 3.02+, ssed, and sed15+, <address2> | ||
648 | * may also be a notation of the form +num, | ||
649 | * indicating the next num lines after <address1> is | ||
650 | * matched. | ||
651 | * GNU sed 4.2.1 accepts even "+" (meaning "+0"). | ||
652 | * We don't (we check for isdigit, see above), think | ||
653 | * about the "+-3" case. | ||
654 | */ | ||
655 | char *end; | ||
656 | /* code is smaller compared to using &cmdstr here: */ | ||
657 | idx = strtol(cmdstr+1, &end, 10); | ||
658 | sed_cmd->end_line = -2 - idx; | ||
659 | cmdstr = end; | ||
660 | } else { | ||
661 | idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match); | ||
662 | cmdstr += idx; | ||
663 | idx--; /* if 0, trigger error check below */ | ||
664 | } | ||
665 | if (idx < 0) | ||
645 | bb_error_msg_and_die("no address after comma"); | 666 | bb_error_msg_and_die("no address after comma"); |
646 | cmdstr += idx; | 667 | sed_cmd->end_line_orig = sed_cmd->end_line; |
647 | } | 668 | } |
648 | 669 | ||
649 | /* skip whitespace before the command */ | 670 | /* skip whitespace before the command */ |
@@ -1089,10 +1110,19 @@ static void process_files(void) | |||
1089 | /* Is this line the end of the current match? */ | 1110 | /* Is this line the end of the current match? */ |
1090 | 1111 | ||
1091 | if (matched) { | 1112 | if (matched) { |
1113 | if (sed_cmd->end_line <= -2) { | ||
1114 | /* address2 is +N, i.e. N lines from beg_line */ | ||
1115 | sed_cmd->end_line = linenum + (-sed_cmd->end_line - 2); | ||
1116 | } | ||
1092 | /* once matched, "n,xxx" range is dead, disabling it */ | 1117 | /* once matched, "n,xxx" range is dead, disabling it */ |
1093 | if (sed_cmd->beg_line > 0) { | 1118 | if (sed_cmd->beg_line > 0) { |
1094 | sed_cmd->beg_line = -2; | 1119 | sed_cmd->beg_line = -2; |
1095 | } | 1120 | } |
1121 | dbg("end1:%d", sed_cmd->end_line ? sed_cmd->end_line == -1 | ||
1122 | ? !next_line : (sed_cmd->end_line <= linenum) | ||
1123 | : !sed_cmd->end_match); | ||
1124 | dbg("end2:%d", sed_cmd->end_match && old_matched | ||
1125 | && !regexec(sed_cmd->end_match,pattern_space, 0, NULL, 0)); | ||
1096 | sed_cmd->in_match = !( | 1126 | sed_cmd->in_match = !( |
1097 | /* has the ending line come, or is this a single address command? */ | 1127 | /* has the ending line come, or is this a single address command? */ |
1098 | (sed_cmd->end_line | 1128 | (sed_cmd->end_line |
@@ -1551,9 +1581,10 @@ int sed_main(int argc UNUSED_PARAM, char **argv) | |||
1551 | free(G.outname); | 1581 | free(G.outname); |
1552 | G.outname = NULL; | 1582 | G.outname = NULL; |
1553 | 1583 | ||
1554 | /* Re-enable disabled range matches */ | 1584 | /* Fix disabled range matches and mangled ",+N" ranges */ |
1555 | for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) { | 1585 | for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) { |
1556 | sed_cmd->beg_line = sed_cmd->beg_line_orig; | 1586 | sed_cmd->beg_line = sed_cmd->beg_line_orig; |
1587 | sed_cmd->end_line = sed_cmd->end_line_orig; | ||
1557 | } | 1588 | } |
1558 | } | 1589 | } |
1559 | /* Here, to handle "sed 'cmds' nonexistent_file" case we did: | 1590 | /* Here, to handle "sed 'cmds' nonexistent_file" case we did: |
diff --git a/editors/vi.c b/editors/vi.c index 926aef19b..3eed279e7 100644 --- a/editors/vi.c +++ b/editors/vi.c | |||
@@ -3723,11 +3723,6 @@ static void do_cmd(int c) | |||
3723 | string_insert(dot, p, ALLOW_UNDO); // insert the string | 3723 | string_insert(dot, p, ALLOW_UNDO); // insert the string |
3724 | end_cmd_q(); // stop adding to q | 3724 | end_cmd_q(); // stop adding to q |
3725 | break; | 3725 | break; |
3726 | #if ENABLE_FEATURE_VI_UNDO | ||
3727 | case 'u': // u- undo last operation | ||
3728 | undo_pop(); | ||
3729 | break; | ||
3730 | #endif | ||
3731 | case 'U': // U- Undo; replace current line with original version | 3726 | case 'U': // U- Undo; replace current line with original version |
3732 | if (reg[Ureg] != NULL) { | 3727 | if (reg[Ureg] != NULL) { |
3733 | p = begin_line(dot); | 3728 | p = begin_line(dot); |
@@ -3739,6 +3734,11 @@ static void do_cmd(int c) | |||
3739 | } | 3734 | } |
3740 | break; | 3735 | break; |
3741 | #endif /* FEATURE_VI_YANKMARK */ | 3736 | #endif /* FEATURE_VI_YANKMARK */ |
3737 | #if ENABLE_FEATURE_VI_UNDO | ||
3738 | case 'u': // u- undo last operation | ||
3739 | undo_pop(); | ||
3740 | break; | ||
3741 | #endif | ||
3742 | case '$': // $- goto end of line | 3742 | case '$': // $- goto end of line |
3743 | case KEYCODE_END: // Cursor Key End | 3743 | case KEYCODE_END: // Cursor Key End |
3744 | for (;;) { | 3744 | for (;;) { |
diff --git a/examples/android-build b/examples/android-build index 89f3b637a..123ba966e 100755 --- a/examples/android-build +++ b/examples/android-build | |||
@@ -29,4 +29,6 @@ else | |||
29 | LDLIBS="dl m c gcc" | 29 | LDLIBS="dl m c gcc" |
30 | fi | 30 | fi |
31 | 31 | ||
32 | # It's possible with newer version | ||
33 | # you need to use CFLAGS_busybox instead of EXTRA_LDFLAGS below: | ||
32 | make EXTRA_LDFLAGS="$LDFLAGS" LDLIBS="$LDLIBS" "$@" | 34 | make EXTRA_LDFLAGS="$LDFLAGS" LDLIBS="$LDLIBS" "$@" |
diff --git a/examples/var_service/ntpd/ntp.script b/examples/var_service/ntpd/ntp.script index 76c34bf74..8542181eb 100755 --- a/examples/var_service/ntpd/ntp.script +++ b/examples/var_service/ntpd/ntp.script | |||
@@ -10,12 +10,30 @@ | |||
10 | 10 | ||
11 | dt=`date '+%Y-%m-%d %H:%M:%S'` | 11 | dt=`date '+%Y-%m-%d %H:%M:%S'` |
12 | 12 | ||
13 | echo "`tail -n 199 -- "$0.log" 2>/dev/null`" >"$0.log.$$" | ||
14 | |||
15 | if test x"$1" = x"unsync" \ | ||
16 | ; then | ||
17 | # No replies for our NTP requests were seen for some time. | ||
18 | # | ||
19 | # Among more mundate cases like network outages, this happens | ||
20 | # if we ran for a LONG time (days) and ntp server's IP has changed. | ||
21 | # ntpd has no code to re-resolve peers' addresses to IPs, | ||
22 | # we need to help it: | ||
23 | # | ||
24 | echo "$dt: $1"\ | ||
25 | "syncronization lost, restarting ntpd"\ | ||
26 | >>"$0.log.$$" | ||
27 | mv -- "$0.log.$$" "$0.log" | ||
28 | kill $PPID | ||
29 | exit | ||
30 | fi | ||
31 | |||
13 | if test x"$stratum" != x"" \ | 32 | if test x"$stratum" != x"" \ |
14 | && test x"$poll_interval" != x"" \ | 33 | && test x"$poll_interval" != x"" \ |
15 | && test 4 -ge "$stratum" \ | 34 | && test 4 -ge "$stratum" \ |
16 | && test 128 -le "$poll_interval" \ | 35 | && test 128 -le "$poll_interval" \ |
17 | ; then | 36 | ; then |
18 | echo "`tail -n 199 -- "$0.log" 2>/dev/null`" >"$0.log.$$" | ||
19 | echo "$dt: $1"\ | 37 | echo "$dt: $1"\ |
20 | "freq_drift_ppm=$freq_drift_ppm"\ | 38 | "freq_drift_ppm=$freq_drift_ppm"\ |
21 | "offset=$offset"\ | 39 | "offset=$offset"\ |
@@ -27,7 +45,6 @@ if test x"$stratum" != x"" \ | |||
27 | exec hwclock --systohc | 45 | exec hwclock --systohc |
28 | fi | 46 | fi |
29 | 47 | ||
30 | echo "`tail -n 199 -- "$0.log" 2>/dev/null`" >"$0.log.$$" | ||
31 | echo "$dt: $1"\ | 48 | echo "$dt: $1"\ |
32 | "freq_drift_ppm=$freq_drift_ppm"\ | 49 | "freq_drift_ppm=$freq_drift_ppm"\ |
33 | "offset=$offset"\ | 50 | "offset=$offset"\ |
diff --git a/findutils/find.c b/findutils/find.c index 83aa63f92..ced8922e7 100644 --- a/findutils/find.c +++ b/findutils/find.c | |||
@@ -1460,12 +1460,10 @@ int find_main(int argc UNUSED_PARAM, char **argv) | |||
1460 | NULL, /* user data */ | 1460 | NULL, /* user data */ |
1461 | 0) /* depth */ | 1461 | 0) /* depth */ |
1462 | ) { | 1462 | ) { |
1463 | status = EXIT_FAILURE; | 1463 | status |= EXIT_FAILURE; |
1464 | goto out; | ||
1465 | } | 1464 | } |
1466 | } | 1465 | } |
1467 | 1466 | ||
1468 | IF_FEATURE_FIND_EXEC_PLUS(status = flush_exec_plus();) | 1467 | IF_FEATURE_FIND_EXEC_PLUS(status |= flush_exec_plus();) |
1469 | out: | ||
1470 | return status; | 1468 | return status; |
1471 | } | 1469 | } |
diff --git a/include/libbb.h b/include/libbb.h index 28ac779c3..2ac95835a 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -84,7 +84,30 @@ | |||
84 | # include <selinux/av_permissions.h> | 84 | # include <selinux/av_permissions.h> |
85 | #endif | 85 | #endif |
86 | #if ENABLE_FEATURE_UTMP | 86 | #if ENABLE_FEATURE_UTMP |
87 | # include <utmp.h> | 87 | # if defined __UCLIBC__ && ( \ |
88 | (UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 32) \ | ||
89 | && UCLIBC_VERSION < KERNEL_VERSION(0, 9, 34) \ | ||
90 | && defined __UCLIBC_HAS_UTMPX__ \ | ||
91 | ) || ( \ | ||
92 | UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 34) \ | ||
93 | ) \ | ||
94 | ) | ||
95 | # include <utmpx.h> | ||
96 | # elif defined __UCLIBC__ | ||
97 | # include <utmp.h> | ||
98 | # define utmpx utmp | ||
99 | # define setutxent setutent | ||
100 | # define endutxent endutent | ||
101 | # define getutxent getutent | ||
102 | # define getutxid getutid | ||
103 | # define getutxline getutline | ||
104 | # define pututxline pututline | ||
105 | # define utmpxname utmpname | ||
106 | # define updwtmpx updwtmp | ||
107 | # define _PATH_UTMPX _PATH_UTMP | ||
108 | # else | ||
109 | # include <utmpx.h> | ||
110 | # endif | ||
88 | #endif | 111 | #endif |
89 | #if ENABLE_LOCALE_SUPPORT | 112 | #if ENABLE_LOCALE_SUPPORT |
90 | # include <locale.h> | 113 | # include <locale.h> |
@@ -726,7 +749,7 @@ void* xrealloc_vector_helper(void *vector, unsigned sizeof_and_shift, int idx) F | |||
726 | 749 | ||
727 | 750 | ||
728 | extern ssize_t safe_read(int fd, void *buf, size_t count) FAST_FUNC; | 751 | extern ssize_t safe_read(int fd, void *buf, size_t count) FAST_FUNC; |
729 | extern ssize_t nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR) FAST_FUNC; | 752 | extern ssize_t nonblock_immune_read(int fd, void *buf, size_t count) FAST_FUNC; |
730 | // NB: will return short read on error, not -1, | 753 | // NB: will return short read on error, not -1, |
731 | // if some data was read before error occurred | 754 | // if some data was read before error occurred |
732 | extern ssize_t full_read(int fd, void *buf, size_t count) FAST_FUNC; | 755 | extern ssize_t full_read(int fd, void *buf, size_t count) FAST_FUNC; |
diff --git a/include/platform.h b/include/platform.h index a550feae6..1cbffc102 100644 --- a/include/platform.h +++ b/include/platform.h | |||
@@ -385,6 +385,7 @@ typedef unsigned smalluint; | |||
385 | #define HAVE_DPRINTF 1 | 385 | #define HAVE_DPRINTF 1 |
386 | #define HAVE_MEMRCHR 1 | 386 | #define HAVE_MEMRCHR 1 |
387 | #define HAVE_MKDTEMP 1 | 387 | #define HAVE_MKDTEMP 1 |
388 | #define HAVE_TTYNAME_R 1 | ||
388 | #define HAVE_PTSNAME_R 1 | 389 | #define HAVE_PTSNAME_R 1 |
389 | #define HAVE_SETBIT 1 | 390 | #define HAVE_SETBIT 1 |
390 | #define HAVE_SIGHANDLER_T 1 | 391 | #define HAVE_SIGHANDLER_T 1 |
@@ -515,9 +516,17 @@ typedef unsigned smalluint; | |||
515 | #endif | 516 | #endif |
516 | 517 | ||
517 | #if defined(ANDROID) || defined(__ANDROID__) | 518 | #if defined(ANDROID) || defined(__ANDROID__) |
518 | # undef HAVE_DPRINTF | 519 | # if __ANDROID_API__ < 8 |
519 | # undef HAVE_GETLINE | 520 | # undef HAVE_DPRINTF |
520 | # undef HAVE_STPCPY | 521 | # else |
522 | # define dprintf fdprintf | ||
523 | # endif | ||
524 | # if __ANDROID_API__ < 21 | ||
525 | # undef HAVE_TTYNAME_R | ||
526 | # undef HAVE_GETLINE | ||
527 | # undef HAVE_STPCPY | ||
528 | # endif | ||
529 | # undef HAVE_MEMPCPY | ||
521 | # undef HAVE_STRCHRNUL | 530 | # undef HAVE_STRCHRNUL |
522 | # undef HAVE_STRVERSCMP | 531 | # undef HAVE_STRVERSCMP |
523 | # undef HAVE_UNLOCKED_LINE_OPS | 532 | # undef HAVE_UNLOCKED_LINE_OPS |
@@ -542,6 +551,11 @@ extern void *memrchr(const void *s, int c, size_t n) FAST_FUNC; | |||
542 | extern char *mkdtemp(char *template) FAST_FUNC; | 551 | extern char *mkdtemp(char *template) FAST_FUNC; |
543 | #endif | 552 | #endif |
544 | 553 | ||
554 | #ifndef HAVE_TTYNAME_R | ||
555 | #define ttyname_r bb_ttyname_r | ||
556 | extern int ttyname_r(int fd, char *buf, size_t buflen); | ||
557 | #endif | ||
558 | |||
545 | #ifndef HAVE_SETBIT | 559 | #ifndef HAVE_SETBIT |
546 | # define setbit(a, b) ((a)[(b) >> 3] |= 1 << ((b) & 7)) | 560 | # define setbit(a, b) ((a)[(b) >> 3] |= 1 << ((b) & 7)) |
547 | # define clrbit(a, b) ((a)[(b) >> 3] &= ~(1 << ((b) & 7))) | 561 | # define clrbit(a, b) ((a)[(b) >> 3] &= ~(1 << ((b) & 7))) |
diff --git a/init/halt.c b/init/halt.c index 7974adb17..ad12d9148 100644 --- a/init/halt.c +++ b/init/halt.c | |||
@@ -74,7 +74,7 @@ | |||
74 | 74 | ||
75 | static void write_wtmp(void) | 75 | static void write_wtmp(void) |
76 | { | 76 | { |
77 | struct utmp utmp; | 77 | struct utmpx utmp; |
78 | struct utsname uts; | 78 | struct utsname uts; |
79 | /* "man utmp" says wtmp file should *not* be created automagically */ | 79 | /* "man utmp" says wtmp file should *not* be created automagically */ |
80 | /*if (access(bb_path_wtmp_file, R_OK|W_OK) == -1) { | 80 | /*if (access(bb_path_wtmp_file, R_OK|W_OK) == -1) { |
@@ -88,7 +88,7 @@ static void write_wtmp(void) | |||
88 | utmp.ut_line[0] = '~'; utmp.ut_line[1] = '~'; /* = strcpy(utmp.ut_line, "~~"); */ | 88 | utmp.ut_line[0] = '~'; utmp.ut_line[1] = '~'; /* = strcpy(utmp.ut_line, "~~"); */ |
89 | uname(&uts); | 89 | uname(&uts); |
90 | safe_strncpy(utmp.ut_host, uts.release, sizeof(utmp.ut_host)); | 90 | safe_strncpy(utmp.ut_host, uts.release, sizeof(utmp.ut_host)); |
91 | updwtmp(bb_path_wtmp_file, &utmp); | 91 | updwtmpx(bb_path_wtmp_file, &utmp); |
92 | } | 92 | } |
93 | #else | 93 | #else |
94 | #define write_wtmp() ((void)0) | 94 | #define write_wtmp() ((void)0) |
diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 3f51ecef6..683d10b20 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c | |||
@@ -635,7 +635,7 @@ static int busybox_main(char **argv) | |||
635 | full_write2_str(bb_banner); /* reuse const string */ | 635 | full_write2_str(bb_banner); /* reuse const string */ |
636 | full_write2_str(" multi-call binary.\n"); /* reuse */ | 636 | full_write2_str(" multi-call binary.\n"); /* reuse */ |
637 | full_write2_str( | 637 | full_write2_str( |
638 | "BusyBox is copyrighted by many authors between 1998-2012.\n" | 638 | "BusyBox is copyrighted by many authors between 1998-2015.\n" |
639 | "Licensed under GPLv2. See source distribution for detailed\n" | 639 | "Licensed under GPLv2. See source distribution for detailed\n" |
640 | "copyright notices.\n" | 640 | "copyright notices.\n" |
641 | "\n" | 641 | "\n" |
@@ -761,23 +761,25 @@ void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv) | |||
761 | xfunc_error_retval = EXIT_FAILURE; | 761 | xfunc_error_retval = EXIT_FAILURE; |
762 | applet_name = APPLET_NAME(applet_no); | 762 | applet_name = APPLET_NAME(applet_no); |
763 | 763 | ||
764 | #if defined APPLET_NO_test | ||
765 | /* Special case. POSIX says "test --help" | 764 | /* Special case. POSIX says "test --help" |
766 | * should be no different from e.g. "test --foo". | 765 | * should be no different from e.g. "test --foo". |
767 | * Thus for "test", we skip --help check. | 766 | * Thus for "test", we skip --help check. |
767 | * "true" and "false" are also special. | ||
768 | */ | 768 | */ |
769 | if (applet_no != APPLET_NO_test) | 769 | if (1 |
770 | #if defined APPLET_NO_test | ||
771 | && applet_no != APPLET_NO_test | ||
772 | #endif | ||
773 | #if defined APPLET_NO_true | ||
774 | && applet_no != APPLET_NO_true | ||
770 | #endif | 775 | #endif |
771 | { | ||
772 | if (argc == 2 && strcmp(argv[1], "--help") == 0) { | ||
773 | #if defined APPLET_NO_false | 776 | #if defined APPLET_NO_false |
774 | /* Someone insisted that "false --help" must exit 1. Sigh */ | 777 | && applet_no != APPLET_NO_false |
775 | if (applet_no != APPLET_NO_false) | ||
776 | #endif | 778 | #endif |
777 | { | 779 | ) { |
778 | /* Make "foo --help" exit with 0: */ | 780 | if (argc == 2 && strcmp(argv[1], "--help") == 0) { |
779 | xfunc_error_retval = 0; | 781 | /* Make "foo --help" exit with 0: */ |
780 | } | 782 | xfunc_error_retval = 0; |
781 | bb_show_usage(); | 783 | bb_show_usage(); |
782 | } | 784 | } |
783 | } | 785 | } |
diff --git a/libbb/change_identity.c b/libbb/change_identity.c index 619db09a8..d48d86326 100644 --- a/libbb/change_identity.c +++ b/libbb/change_identity.c | |||
@@ -33,9 +33,28 @@ | |||
33 | /* Become the user and group(s) specified by PW. */ | 33 | /* Become the user and group(s) specified by PW. */ |
34 | void FAST_FUNC change_identity(const struct passwd *pw) | 34 | void FAST_FUNC change_identity(const struct passwd *pw) |
35 | { | 35 | { |
36 | if (initgroups(pw->pw_name, pw->pw_gid) == -1) | 36 | int res; |
37 | bb_perror_msg_and_die("can't set groups"); | 37 | |
38 | res = initgroups(pw->pw_name, pw->pw_gid); | ||
38 | endgrent(); /* helps to close a fd used internally by libc */ | 39 | endgrent(); /* helps to close a fd used internally by libc */ |
40 | |||
41 | if (res != 0) { | ||
42 | /* | ||
43 | * If initgroups() fails because a system call is unimplemented | ||
44 | * then we are running on a Linux kernel compiled without multiuser | ||
45 | * support (CONFIG_MULTIUSER is not defined). | ||
46 | * | ||
47 | * If we are running without multiuser support *and* the target uid | ||
48 | * already matches the current uid then we can skip the change of | ||
49 | * identity. | ||
50 | */ | ||
51 | if (errno == ENOSYS && pw->pw_uid == getuid()) { | ||
52 | return; | ||
53 | } | ||
54 | |||
55 | bb_perror_msg_and_die("can't set groups"); | ||
56 | } | ||
57 | |||
39 | xsetgid(pw->pw_gid); | 58 | xsetgid(pw->pw_gid); |
40 | xsetuid(pw->pw_uid); | 59 | xsetuid(pw->pw_uid); |
41 | } | 60 | } |
diff --git a/libbb/missing_syscalls.c b/libbb/missing_syscalls.c index dd430e3e2..e3c1e924b 100644 --- a/libbb/missing_syscalls.c +++ b/libbb/missing_syscalls.c | |||
@@ -39,4 +39,9 @@ int pivot_root(const char *new_root, const char *put_old) | |||
39 | { | 39 | { |
40 | return syscall(__NR_pivot_root, new_root, put_old); | 40 | return syscall(__NR_pivot_root, new_root, put_old); |
41 | } | 41 | } |
42 | |||
43 | int tcdrain(int fd) | ||
44 | { | ||
45 | return ioctl(fd, TCSBRK, 1); | ||
46 | } | ||
42 | #endif | 47 | #endif |
diff --git a/libbb/platform.c b/libbb/platform.c index 8d90ca4e9..03bbb798b 100644 --- a/libbb/platform.c +++ b/libbb/platform.c | |||
@@ -194,3 +194,22 @@ ssize_t FAST_FUNC getline(char **lineptr, size_t *n, FILE *stream) | |||
194 | return len; | 194 | return len; |
195 | } | 195 | } |
196 | #endif | 196 | #endif |
197 | |||
198 | #ifndef HAVE_TTYNAME_R | ||
199 | int ttyname_r(int fd, char *buf, size_t buflen) | ||
200 | { | ||
201 | int r; | ||
202 | char path[sizeof("/proc/self/fd/%d") + sizeof(int)*3]; | ||
203 | |||
204 | if (!isatty(fd)) | ||
205 | return errno == EINVAL ? ENOTTY : errno; | ||
206 | sprintf(path, "/proc/self/fd/%d", fd); | ||
207 | r = readlink(path, buf, buflen); | ||
208 | if (r < 0) | ||
209 | return errno; | ||
210 | if (r >= buflen) | ||
211 | return ERANGE; | ||
212 | buf[r] = '\0'; | ||
213 | return 0; | ||
214 | } | ||
215 | #endif | ||
diff --git a/libbb/read_printf.c b/libbb/read_printf.c index ef4911cd5..e47ac7afe 100644 --- a/libbb/read_printf.c +++ b/libbb/read_printf.c | |||
@@ -45,20 +45,20 @@ | |||
45 | * which detects EAGAIN and uses poll() to wait on the fd. | 45 | * which detects EAGAIN and uses poll() to wait on the fd. |
46 | * Thankfully, poll() doesn't care about O_NONBLOCK flag. | 46 | * Thankfully, poll() doesn't care about O_NONBLOCK flag. |
47 | */ | 47 | */ |
48 | ssize_t FAST_FUNC nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR) | 48 | ssize_t FAST_FUNC nonblock_immune_read(int fd, void *buf, size_t count) |
49 | { | 49 | { |
50 | struct pollfd pfd[1]; | 50 | struct pollfd pfd[1]; |
51 | ssize_t n; | 51 | ssize_t n; |
52 | 52 | ||
53 | while (1) { | 53 | while (1) { |
54 | n = loop_on_EINTR ? safe_read(fd, buf, count) : read(fd, buf, count); | 54 | n = safe_read(fd, buf, count); |
55 | if (n >= 0 || errno != EAGAIN) | 55 | if (n >= 0 || errno != EAGAIN) |
56 | return n; | 56 | return n; |
57 | /* fd is in O_NONBLOCK mode. Wait using poll and repeat */ | 57 | /* fd is in O_NONBLOCK mode. Wait using poll and repeat */ |
58 | pfd[0].fd = fd; | 58 | pfd[0].fd = fd; |
59 | pfd[0].events = POLLIN; | 59 | pfd[0].events = POLLIN; |
60 | /* note: safe_poll pulls in printf */ | 60 | /* note: safe_poll pulls in printf */ |
61 | loop_on_EINTR ? safe_poll(pfd, 1, -1) : poll(pfd, 1, -1); | 61 | safe_poll(pfd, 1, -1); |
62 | } | 62 | } |
63 | } | 63 | } |
64 | 64 | ||
@@ -81,7 +81,7 @@ char* FAST_FUNC xmalloc_reads(int fd, size_t *maxsz_p) | |||
81 | p = buf + sz; | 81 | p = buf + sz; |
82 | sz += 128; | 82 | sz += 128; |
83 | } | 83 | } |
84 | if (nonblock_immune_read(fd, p, 1, /*loop_on_EINTR:*/ 1) != 1) { | 84 | if (nonblock_immune_read(fd, p, 1) != 1) { |
85 | /* EOF/error */ | 85 | /* EOF/error */ |
86 | if (p == buf) { /* we read nothing */ | 86 | if (p == buf) { /* we read nothing */ |
87 | free(buf); | 87 | free(buf); |
diff --git a/libbb/utmp.c b/libbb/utmp.c index 8ad9ba27e..bd07670db 100644 --- a/libbb/utmp.c +++ b/libbb/utmp.c | |||
@@ -16,7 +16,7 @@ static void touch(const char *filename) | |||
16 | 16 | ||
17 | void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname) | 17 | void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname) |
18 | { | 18 | { |
19 | struct utmp utent; | 19 | struct utmpx utent; |
20 | char *id; | 20 | char *id; |
21 | unsigned width; | 21 | unsigned width; |
22 | 22 | ||
@@ -45,17 +45,17 @@ void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, con | |||
45 | tty_name += 3; | 45 | tty_name += 3; |
46 | strncpy(id, tty_name, width); | 46 | strncpy(id, tty_name, width); |
47 | 47 | ||
48 | touch(_PATH_UTMP); | 48 | touch(_PATH_UTMPX); |
49 | //utmpname(_PATH_UTMP); | 49 | //utmpxname(_PATH_UTMPX); |
50 | setutent(); | 50 | setutxent(); |
51 | /* Append new one (hopefully, unless we collide on ut_id) */ | 51 | /* Append new one (hopefully, unless we collide on ut_id) */ |
52 | pututline(&utent); | 52 | pututxline(&utent); |
53 | endutent(); | 53 | endutxent(); |
54 | 54 | ||
55 | #if ENABLE_FEATURE_WTMP | 55 | #if ENABLE_FEATURE_WTMP |
56 | /* "man utmp" says wtmp file should *not* be created automagically */ | 56 | /* "man utmp" says wtmp file should *not* be created automagically */ |
57 | /*touch(bb_path_wtmp_file);*/ | 57 | /*touch(bb_path_wtmp_file);*/ |
58 | updwtmp(bb_path_wtmp_file, &utent); | 58 | updwtmpx(bb_path_wtmp_file, &utent); |
59 | #endif | 59 | #endif |
60 | } | 60 | } |
61 | 61 | ||
@@ -64,17 +64,17 @@ void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, con | |||
64 | */ | 64 | */ |
65 | void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname) | 65 | void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname) |
66 | { | 66 | { |
67 | struct utmp utent; | 67 | struct utmpx utent; |
68 | struct utmp *utp; | 68 | struct utmpx *utp; |
69 | 69 | ||
70 | touch(_PATH_UTMP); | 70 | touch(_PATH_UTMPX); |
71 | //utmpname(_PATH_UTMP); | 71 | //utmpxname(_PATH_UTMPX); |
72 | setutent(); | 72 | setutxent(); |
73 | 73 | ||
74 | /* Did init/getty/telnetd/sshd/... create an entry for us? | 74 | /* Did init/getty/telnetd/sshd/... create an entry for us? |
75 | * It should be (new_type-1), but we'd also reuse | 75 | * It should be (new_type-1), but we'd also reuse |
76 | * any other potentially stale xxx_PROCESS entry */ | 76 | * any other potentially stale xxx_PROCESS entry */ |
77 | while ((utp = getutent()) != NULL) { | 77 | while ((utp = getutxent()) != NULL) { |
78 | if (utp->ut_pid == pid | 78 | if (utp->ut_pid == pid |
79 | // && ut->ut_line[0] | 79 | // && ut->ut_line[0] |
80 | && utp->ut_id[0] /* must have nonzero id */ | 80 | && utp->ut_id[0] /* must have nonzero id */ |
@@ -88,25 +88,25 @@ void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const | |||
88 | /* Stale record. Nuke hostname */ | 88 | /* Stale record. Nuke hostname */ |
89 | memset(utp->ut_host, 0, sizeof(utp->ut_host)); | 89 | memset(utp->ut_host, 0, sizeof(utp->ut_host)); |
90 | } | 90 | } |
91 | /* NB: pututline (see later) searches for matching utent | 91 | /* NB: pututxline (see later) searches for matching utxent |
92 | * using getutid(utent) - we must not change ut_id | 92 | * using getutxid(utent) - we must not change ut_id |
93 | * if we want *exactly this* record to be overwritten! | 93 | * if we want *exactly this* record to be overwritten! |
94 | */ | 94 | */ |
95 | break; | 95 | break; |
96 | } | 96 | } |
97 | } | 97 | } |
98 | //endutent(); - no need, pututline can deal with (and actually likes) | 98 | //endutxent(); - no need, pututxline can deal with (and actually likes) |
99 | //the situation when utmp file is positioned on found record | 99 | //the situation when utmp file is positioned on found record |
100 | 100 | ||
101 | if (!utp) { | 101 | if (!utp) { |
102 | if (new_type != DEAD_PROCESS) | 102 | if (new_type != DEAD_PROCESS) |
103 | write_new_utmp(pid, new_type, tty_name, username, hostname); | 103 | write_new_utmp(pid, new_type, tty_name, username, hostname); |
104 | else | 104 | else |
105 | endutent(); | 105 | endutxent(); |
106 | return; | 106 | return; |
107 | } | 107 | } |
108 | 108 | ||
109 | /* Make a copy. We can't use *utp, pututline's internal getutid | 109 | /* Make a copy. We can't use *utp, pututxline's internal getutxid |
110 | * will overwrite it before it is used! */ | 110 | * will overwrite it before it is used! */ |
111 | utent = *utp; | 111 | utent = *utp; |
112 | 112 | ||
@@ -120,14 +120,14 @@ void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const | |||
120 | utent.ut_tv.tv_sec = time(NULL); | 120 | utent.ut_tv.tv_sec = time(NULL); |
121 | 121 | ||
122 | /* Update, or append new one */ | 122 | /* Update, or append new one */ |
123 | //setutent(); | 123 | //setutxent(); |
124 | pututline(&utent); | 124 | pututxline(&utent); |
125 | endutent(); | 125 | endutxent(); |
126 | 126 | ||
127 | #if ENABLE_FEATURE_WTMP | 127 | #if ENABLE_FEATURE_WTMP |
128 | /* "man utmp" says wtmp file should *not* be created automagically */ | 128 | /* "man utmp" says wtmp file should *not* be created automagically */ |
129 | /*touch(bb_path_wtmp_file);*/ | 129 | /*touch(bb_path_wtmp_file);*/ |
130 | updwtmp(bb_path_wtmp_file, &utent); | 130 | updwtmpx(bb_path_wtmp_file, &utent); |
131 | #endif | 131 | #endif |
132 | } | 132 | } |
133 | 133 | ||
diff --git a/miscutils/i2c_tools.c b/miscutils/i2c_tools.c index 03bb03974..7034dc9a8 100644 --- a/miscutils/i2c_tools.c +++ b/miscutils/i2c_tools.c | |||
@@ -379,8 +379,12 @@ static int i2c_dev_open(int i2cbus) | |||
379 | sprintf(filename, "/dev/i2c-%d", i2cbus); | 379 | sprintf(filename, "/dev/i2c-%d", i2cbus); |
380 | fd = open(filename, O_RDWR); | 380 | fd = open(filename, O_RDWR); |
381 | if (fd < 0) { | 381 | if (fd < 0) { |
382 | filename[8] = '/'; /* change to "/dev/i2c/%d" */ | 382 | if (errno == ENOENT) { |
383 | fd = xopen(filename, O_RDWR); | 383 | filename[8] = '/'; /* change to "/dev/i2c/%d" */ |
384 | fd = xopen(filename, O_RDWR); | ||
385 | } else { | ||
386 | bb_perror_msg_and_die("can't open '%s'", filename); | ||
387 | } | ||
384 | } | 388 | } |
385 | 389 | ||
386 | return fd; | 390 | return fd; |
@@ -1284,13 +1288,16 @@ int i2cdetect_main(int argc UNUSED_PARAM, char **argv) | |||
1284 | unsigned opts; | 1288 | unsigned opts; |
1285 | 1289 | ||
1286 | opt_complementary = "q--r:r--q:" /* mutually exclusive */ | 1290 | opt_complementary = "q--r:r--q:" /* mutually exclusive */ |
1287 | "-1:?3"; /* from 1 to 3 args */ | 1291 | "?3"; /* up to 3 args */ |
1288 | opts = getopt32(argv, optstr); | 1292 | opts = getopt32(argv, optstr); |
1289 | argv += optind; | 1293 | argv += optind; |
1290 | 1294 | ||
1291 | if (opts & opt_l) | 1295 | if (opts & opt_l) |
1292 | list_i2c_busses_and_exit(); | 1296 | list_i2c_busses_and_exit(); |
1293 | 1297 | ||
1298 | if (!argv[0]) | ||
1299 | bb_show_usage(); | ||
1300 | |||
1294 | bus_num = i2c_bus_lookup(argv[0]); | 1301 | bus_num = i2c_bus_lookup(argv[0]); |
1295 | fd = i2c_dev_open(bus_num); | 1302 | fd = i2c_dev_open(bus_num); |
1296 | get_funcs_matrix(fd, &funcs); | 1303 | get_funcs_matrix(fd, &funcs); |
diff --git a/miscutils/last.c b/miscutils/last.c index a144c7e47..6d8b58463 100644 --- a/miscutils/last.c +++ b/miscutils/last.c | |||
@@ -32,21 +32,21 @@ | |||
32 | 32 | ||
33 | #if defined UT_LINESIZE \ | 33 | #if defined UT_LINESIZE \ |
34 | && ((UT_LINESIZE != 32) || (UT_NAMESIZE != 32) || (UT_HOSTSIZE != 256)) | 34 | && ((UT_LINESIZE != 32) || (UT_NAMESIZE != 32) || (UT_HOSTSIZE != 256)) |
35 | #error struct utmp member char[] size(s) have changed! | 35 | #error struct utmpx member char[] size(s) have changed! |
36 | #elif defined __UT_LINESIZE \ | 36 | #elif defined __UT_LINESIZE \ |
37 | && ((__UT_LINESIZE != 32) || (__UT_NAMESIZE != 64) || (__UT_HOSTSIZE != 256)) | 37 | && ((__UT_LINESIZE != 32) || (__UT_NAMESIZE != 64) || (__UT_HOSTSIZE != 256)) |
38 | #error struct utmp member char[] size(s) have changed! | 38 | #error struct utmpx member char[] size(s) have changed! |
39 | #endif | 39 | #endif |
40 | 40 | ||
41 | #if EMPTY != 0 || RUN_LVL != 1 || BOOT_TIME != 2 || NEW_TIME != 3 || \ | 41 | #if EMPTY != 0 || RUN_LVL != 1 || BOOT_TIME != 2 || NEW_TIME != 3 || \ |
42 | OLD_TIME != 4 | 42 | OLD_TIME != 4 |
43 | #error Values for the ut_type field of struct utmp changed | 43 | #error Values for the ut_type field of struct utmpx changed |
44 | #endif | 44 | #endif |
45 | 45 | ||
46 | int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 46 | int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
47 | int last_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | 47 | int last_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
48 | { | 48 | { |
49 | struct utmp ut; | 49 | struct utmpx ut; |
50 | int n, file = STDIN_FILENO; | 50 | int n, file = STDIN_FILENO; |
51 | time_t t_tmp; | 51 | time_t t_tmp; |
52 | off_t pos; | 52 | off_t pos; |
diff --git a/miscutils/last_fancy.c b/miscutils/last_fancy.c index 16ed9e920..8194e31b5 100644 --- a/miscutils/last_fancy.c +++ b/miscutils/last_fancy.c | |||
@@ -22,6 +22,10 @@ | |||
22 | #define HEADER_LINE_WIDE "USER", "TTY", \ | 22 | #define HEADER_LINE_WIDE "USER", "TTY", \ |
23 | INET6_ADDRSTRLEN, INET6_ADDRSTRLEN, "HOST", "LOGIN", " TIME", "" | 23 | INET6_ADDRSTRLEN, INET6_ADDRSTRLEN, "HOST", "LOGIN", " TIME", "" |
24 | 24 | ||
25 | #if !defined __UT_LINESIZE && defined UT_LINESIZE | ||
26 | # define __UT_LINESIZE UT_LINESIZE | ||
27 | #endif | ||
28 | |||
25 | enum { | 29 | enum { |
26 | NORMAL, | 30 | NORMAL, |
27 | LOGGED, | 31 | LOGGED, |
@@ -39,7 +43,7 @@ enum { | |||
39 | 43 | ||
40 | #define show_wide (option_mask32 & LAST_OPT_W) | 44 | #define show_wide (option_mask32 & LAST_OPT_W) |
41 | 45 | ||
42 | static void show_entry(struct utmp *ut, int state, time_t dur_secs) | 46 | static void show_entry(struct utmpx *ut, int state, time_t dur_secs) |
43 | { | 47 | { |
44 | unsigned days, hours, mins; | 48 | unsigned days, hours, mins; |
45 | char duration[sizeof("(%u+02:02)") + sizeof(int)*3]; | 49 | char duration[sizeof("(%u+02:02)") + sizeof(int)*3]; |
@@ -104,7 +108,7 @@ static void show_entry(struct utmp *ut, int state, time_t dur_secs) | |||
104 | duration_str); | 108 | duration_str); |
105 | } | 109 | } |
106 | 110 | ||
107 | static int get_ut_type(struct utmp *ut) | 111 | static int get_ut_type(struct utmpx *ut) |
108 | { | 112 | { |
109 | if (ut->ut_line[0] == '~') { | 113 | if (ut->ut_line[0] == '~') { |
110 | if (strcmp(ut->ut_user, "shutdown") == 0) { | 114 | if (strcmp(ut->ut_user, "shutdown") == 0) { |
@@ -142,7 +146,7 @@ static int get_ut_type(struct utmp *ut) | |||
142 | return ut->ut_type; | 146 | return ut->ut_type; |
143 | } | 147 | } |
144 | 148 | ||
145 | static int is_runlevel_shutdown(struct utmp *ut) | 149 | static int is_runlevel_shutdown(struct utmpx *ut) |
146 | { | 150 | { |
147 | if (((ut->ut_pid & 255) == '0') || ((ut->ut_pid & 255) == '6')) { | 151 | if (((ut->ut_pid & 255) == '0') || ((ut->ut_pid & 255) == '6')) { |
148 | return 1; | 152 | return 1; |
@@ -154,7 +158,7 @@ static int is_runlevel_shutdown(struct utmp *ut) | |||
154 | int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 158 | int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
155 | int last_main(int argc UNUSED_PARAM, char **argv) | 159 | int last_main(int argc UNUSED_PARAM, char **argv) |
156 | { | 160 | { |
157 | struct utmp ut; | 161 | struct utmpx ut; |
158 | const char *filename = _PATH_WTMP; | 162 | const char *filename = _PATH_WTMP; |
159 | llist_t *zlist; | 163 | llist_t *zlist; |
160 | off_t pos; | 164 | off_t pos; |
@@ -242,9 +246,9 @@ int last_main(int argc UNUSED_PARAM, char **argv) | |||
242 | { | 246 | { |
243 | llist_t *el, *next; | 247 | llist_t *el, *next; |
244 | for (el = zlist; el; el = next) { | 248 | for (el = zlist; el; el = next) { |
245 | struct utmp *up = (struct utmp *)el->data; | 249 | struct utmpx *up = (struct utmpx *)el->data; |
246 | next = el->link; | 250 | next = el->link; |
247 | if (strncmp(up->ut_line, ut.ut_line, UT_LINESIZE) == 0) { | 251 | if (strncmp(up->ut_line, ut.ut_line, __UT_LINESIZE) == 0) { |
248 | if (show) { | 252 | if (show) { |
249 | show_entry(&ut, NORMAL, up->ut_tv.tv_sec); | 253 | show_entry(&ut, NORMAL, up->ut_tv.tv_sec); |
250 | show = 0; | 254 | show = 0; |
diff --git a/miscutils/runlevel.c b/miscutils/runlevel.c index 76231df22..8558db862 100644 --- a/miscutils/runlevel.c +++ b/miscutils/runlevel.c | |||
@@ -29,19 +29,19 @@ | |||
29 | int runlevel_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 29 | int runlevel_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
30 | int runlevel_main(int argc UNUSED_PARAM, char **argv) | 30 | int runlevel_main(int argc UNUSED_PARAM, char **argv) |
31 | { | 31 | { |
32 | struct utmp *ut; | 32 | struct utmpx *ut; |
33 | char prev; | 33 | char prev; |
34 | 34 | ||
35 | if (argv[1]) utmpname(argv[1]); | 35 | if (argv[1]) utmpxname(argv[1]); |
36 | 36 | ||
37 | setutent(); | 37 | setutxent(); |
38 | while ((ut = getutent()) != NULL) { | 38 | while ((ut = getutxent()) != NULL) { |
39 | if (ut->ut_type == RUN_LVL) { | 39 | if (ut->ut_type == RUN_LVL) { |
40 | prev = ut->ut_pid / 256; | 40 | prev = ut->ut_pid / 256; |
41 | if (prev == 0) prev = 'N'; | 41 | if (prev == 0) prev = 'N'; |
42 | printf("%c %c\n", prev, ut->ut_pid % 256); | 42 | printf("%c %c\n", prev, ut->ut_pid % 256); |
43 | if (ENABLE_FEATURE_CLEAN_UP) | 43 | if (ENABLE_FEATURE_CLEAN_UP) |
44 | endutent(); | 44 | endutxent(); |
45 | return 0; | 45 | return 0; |
46 | } | 46 | } |
47 | } | 47 | } |
@@ -49,6 +49,6 @@ int runlevel_main(int argc UNUSED_PARAM, char **argv) | |||
49 | puts("unknown"); | 49 | puts("unknown"); |
50 | 50 | ||
51 | if (ENABLE_FEATURE_CLEAN_UP) | 51 | if (ENABLE_FEATURE_CLEAN_UP) |
52 | endutent(); | 52 | endutxent(); |
53 | return 1; | 53 | return 1; |
54 | } | 54 | } |
diff --git a/miscutils/wall.c b/miscutils/wall.c index bb709ee39..50658f457 100644 --- a/miscutils/wall.c +++ b/miscutils/wall.c | |||
@@ -32,7 +32,7 @@ | |||
32 | int wall_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 32 | int wall_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
33 | int wall_main(int argc UNUSED_PARAM, char **argv) | 33 | int wall_main(int argc UNUSED_PARAM, char **argv) |
34 | { | 34 | { |
35 | struct utmp *ut; | 35 | struct utmpx *ut; |
36 | char *msg; | 36 | char *msg; |
37 | int fd; | 37 | int fd; |
38 | 38 | ||
@@ -46,8 +46,8 @@ int wall_main(int argc UNUSED_PARAM, char **argv) | |||
46 | msg = xmalloc_read(fd, NULL); | 46 | msg = xmalloc_read(fd, NULL); |
47 | if (ENABLE_FEATURE_CLEAN_UP && argv[1]) | 47 | if (ENABLE_FEATURE_CLEAN_UP && argv[1]) |
48 | close(fd); | 48 | close(fd); |
49 | setutent(); | 49 | setutxent(); |
50 | while ((ut = getutent()) != NULL) { | 50 | while ((ut = getutxent()) != NULL) { |
51 | char *line; | 51 | char *line; |
52 | if (ut->ut_type != USER_PROCESS) | 52 | if (ut->ut_type != USER_PROCESS) |
53 | continue; | 53 | continue; |
@@ -56,7 +56,7 @@ int wall_main(int argc UNUSED_PARAM, char **argv) | |||
56 | free(line); | 56 | free(line); |
57 | } | 57 | } |
58 | if (ENABLE_FEATURE_CLEAN_UP) { | 58 | if (ENABLE_FEATURE_CLEAN_UP) { |
59 | endutent(); | 59 | endutxent(); |
60 | free(msg); | 60 | free(msg); |
61 | } | 61 | } |
62 | return EXIT_SUCCESS; | 62 | return EXIT_SUCCESS; |
diff --git a/networking/ftpd.c b/networking/ftpd.c index 0c10e1f25..2351d6dd3 100644 --- a/networking/ftpd.c +++ b/networking/ftpd.c | |||
@@ -1116,6 +1116,9 @@ int ftpd_main(int argc, char **argv) | |||
1116 | int ftpd_main(int argc UNUSED_PARAM, char **argv) | 1116 | int ftpd_main(int argc UNUSED_PARAM, char **argv) |
1117 | #endif | 1117 | #endif |
1118 | { | 1118 | { |
1119 | #if ENABLE_FEATURE_FTP_AUTHENTICATION | ||
1120 | struct passwd *pw = NULL; | ||
1121 | #endif | ||
1119 | unsigned abs_timeout; | 1122 | unsigned abs_timeout; |
1120 | unsigned verbose_S; | 1123 | unsigned verbose_S; |
1121 | smallint opts; | 1124 | smallint opts; |
@@ -1193,29 +1196,23 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) | |||
1193 | signal(SIGALRM, timeout_handler); | 1196 | signal(SIGALRM, timeout_handler); |
1194 | 1197 | ||
1195 | #if ENABLE_FEATURE_FTP_AUTHENTICATION | 1198 | #if ENABLE_FEATURE_FTP_AUTHENTICATION |
1196 | { | 1199 | while (1) { |
1197 | struct passwd *pw = NULL; | 1200 | uint32_t cmdval = cmdio_get_cmd_and_arg(); |
1198 | |||
1199 | while (1) { | ||
1200 | uint32_t cmdval = cmdio_get_cmd_and_arg(); | ||
1201 | |||
1202 | if (cmdval == const_USER) { | 1201 | if (cmdval == const_USER) { |
1203 | pw = getpwnam(G.ftp_arg); | 1202 | pw = getpwnam(G.ftp_arg); |
1204 | cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify password\r\n"); | 1203 | cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify password\r\n"); |
1205 | } else if (cmdval == const_PASS) { | 1204 | } else if (cmdval == const_PASS) { |
1206 | if (check_password(pw, G.ftp_arg) > 0) { | 1205 | if (check_password(pw, G.ftp_arg) > 0) { |
1207 | break; /* login success */ | 1206 | break; /* login success */ |
1208 | } | ||
1209 | cmdio_write_raw(STR(FTP_LOGINERR)" Login failed\r\n"); | ||
1210 | pw = NULL; | ||
1211 | } else if (cmdval == const_QUIT) { | ||
1212 | WRITE_OK(FTP_GOODBYE); | ||
1213 | return 0; | ||
1214 | } else { | ||
1215 | cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n"); | ||
1216 | } | 1207 | } |
1208 | cmdio_write_raw(STR(FTP_LOGINERR)" Login failed\r\n"); | ||
1209 | pw = NULL; | ||
1210 | } else if (cmdval == const_QUIT) { | ||
1211 | WRITE_OK(FTP_GOODBYE); | ||
1212 | return 0; | ||
1213 | } else { | ||
1214 | cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n"); | ||
1217 | } | 1215 | } |
1218 | change_identity(pw); | ||
1219 | } | 1216 | } |
1220 | WRITE_OK(FTP_LOGINOK); | 1217 | WRITE_OK(FTP_LOGINOK); |
1221 | #endif | 1218 | #endif |
@@ -1233,6 +1230,10 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) | |||
1233 | xchroot(argv[0]); | 1230 | xchroot(argv[0]); |
1234 | } | 1231 | } |
1235 | 1232 | ||
1233 | #if ENABLE_FEATURE_FTP_AUTHENTICATION | ||
1234 | change_identity(pw); | ||
1235 | #endif | ||
1236 | |||
1236 | /* RFC-959 Section 5.1 | 1237 | /* RFC-959 Section 5.1 |
1237 | * The following commands and options MUST be supported by every | 1238 | * The following commands and options MUST be supported by every |
1238 | * server-FTP and user-FTP, except in cases where the underlying | 1239 | * server-FTP and user-FTP, except in cases where the underlying |
diff --git a/procps/uptime.c b/procps/uptime.c index 778812a6f..149bae6e5 100644 --- a/procps/uptime.c +++ b/procps/uptime.c | |||
@@ -81,10 +81,10 @@ int uptime_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
81 | 81 | ||
82 | #if ENABLE_FEATURE_UPTIME_UTMP_SUPPORT | 82 | #if ENABLE_FEATURE_UPTIME_UTMP_SUPPORT |
83 | { | 83 | { |
84 | struct utmp *ut; | 84 | struct utmpx *ut; |
85 | unsigned users = 0; | 85 | unsigned users = 0; |
86 | while ((ut = getutent()) != NULL) { | 86 | while ((ut = getutxent()) != NULL) { |
87 | if ((ut->ut_type == USER_PROCESS) && (ut->ut_name[0] != '\0')) | 87 | if ((ut->ut_type == USER_PROCESS) && (ut->ut_user[0] != '\0')) |
88 | users++; | 88 | users++; |
89 | } | 89 | } |
90 | printf(", %u users", users); | 90 | printf(", %u users", users); |
diff --git a/shell/ash.c b/shell/ash.c index 8cc3f0872..578904478 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -666,8 +666,6 @@ out2str(const char *p) | |||
666 | #define CTLVAR ((unsigned char)'\202') /* variable defn */ | 666 | #define CTLVAR ((unsigned char)'\202') /* variable defn */ |
667 | #define CTLENDVAR ((unsigned char)'\203') | 667 | #define CTLENDVAR ((unsigned char)'\203') |
668 | #define CTLBACKQ ((unsigned char)'\204') | 668 | #define CTLBACKQ ((unsigned char)'\204') |
669 | #define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ | ||
670 | /* CTLBACKQ | CTLQUOTE == '\205' */ | ||
671 | #define CTLARI ((unsigned char)'\206') /* arithmetic expression */ | 669 | #define CTLARI ((unsigned char)'\206') /* arithmetic expression */ |
672 | #define CTLENDARI ((unsigned char)'\207') | 670 | #define CTLENDARI ((unsigned char)'\207') |
673 | #define CTLQUOTEMARK ((unsigned char)'\210') | 671 | #define CTLQUOTEMARK ((unsigned char)'\210') |
@@ -676,7 +674,6 @@ out2str(const char *p) | |||
676 | /* variable substitution byte (follows CTLVAR) */ | 674 | /* variable substitution byte (follows CTLVAR) */ |
677 | #define VSTYPE 0x0f /* type of variable substitution */ | 675 | #define VSTYPE 0x0f /* type of variable substitution */ |
678 | #define VSNUL 0x10 /* colon--treat the empty string as unset */ | 676 | #define VSNUL 0x10 /* colon--treat the empty string as unset */ |
679 | #define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ | ||
680 | 677 | ||
681 | /* values of VSTYPE field */ | 678 | /* values of VSTYPE field */ |
682 | #define VSNORMAL 0x1 /* normal variable: $var or ${var} */ | 679 | #define VSNORMAL 0x1 /* normal variable: $var or ${var} */ |
@@ -696,8 +693,9 @@ out2str(const char *p) | |||
696 | #endif | 693 | #endif |
697 | 694 | ||
698 | static const char dolatstr[] ALIGN1 = { | 695 | static const char dolatstr[] ALIGN1 = { |
699 | CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' | 696 | CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0' |
700 | }; | 697 | }; |
698 | #define DOLATSTRLEN 6 | ||
701 | 699 | ||
702 | #define NCMD 0 | 700 | #define NCMD 0 |
703 | #define NPIPE 1 | 701 | #define NPIPE 1 |
@@ -933,9 +931,7 @@ trace_puts_quoted(char *s) | |||
933 | case '\\': c = '\\'; goto backslash; | 931 | case '\\': c = '\\'; goto backslash; |
934 | case CTLESC: c = 'e'; goto backslash; | 932 | case CTLESC: c = 'e'; goto backslash; |
935 | case CTLVAR: c = 'v'; goto backslash; | 933 | case CTLVAR: c = 'v'; goto backslash; |
936 | case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; | ||
937 | case CTLBACKQ: c = 'q'; goto backslash; | 934 | case CTLBACKQ: c = 'q'; goto backslash; |
938 | case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; | ||
939 | backslash: | 935 | backslash: |
940 | putc('\\', tracefile); | 936 | putc('\\', tracefile); |
941 | putc(c, tracefile); | 937 | putc(c, tracefile); |
@@ -1098,7 +1094,6 @@ sharg(union node *arg, FILE *fp) | |||
1098 | putc('}', fp); | 1094 | putc('}', fp); |
1099 | break; | 1095 | break; |
1100 | case CTLBACKQ: | 1096 | case CTLBACKQ: |
1101 | case CTLBACKQ|CTLQUOTE: | ||
1102 | putc('$', fp); | 1097 | putc('$', fp); |
1103 | putc('(', fp); | 1098 | putc('(', fp); |
1104 | shtree(bqlist->n, -1, NULL, fp); | 1099 | shtree(bqlist->n, -1, NULL, fp); |
@@ -2098,7 +2093,7 @@ varcmp(const char *p, const char *q) | |||
2098 | int c, d; | 2093 | int c, d; |
2099 | 2094 | ||
2100 | while ((c = *p) == (d = *q)) { | 2095 | while ((c = *p) == (d = *q)) { |
2101 | if (!c || c == '=') | 2096 | if (c == '\0' || c == '=') |
2102 | goto out; | 2097 | goto out; |
2103 | p++; | 2098 | p++; |
2104 | q++; | 2099 | q++; |
@@ -2315,7 +2310,7 @@ setvar(const char *name, const char *val, int flags) | |||
2315 | } | 2310 | } |
2316 | 2311 | ||
2317 | static void FAST_FUNC | 2312 | static void FAST_FUNC |
2318 | setvar2(const char *name, const char *val) | 2313 | setvar0(const char *name, const char *val) |
2319 | { | 2314 | { |
2320 | setvar(name, val, 0); | 2315 | setvar(name, val, 0); |
2321 | } | 2316 | } |
@@ -2378,7 +2373,7 @@ unsetvar(const char *s) | |||
2378 | free(vp); | 2373 | free(vp); |
2379 | INT_ON; | 2374 | INT_ON; |
2380 | } else { | 2375 | } else { |
2381 | setvar2(s, 0); | 2376 | setvar0(s, NULL); |
2382 | vp->flags &= ~VEXPORT; | 2377 | vp->flags &= ~VEXPORT; |
2383 | } | 2378 | } |
2384 | ok: | 2379 | ok: |
@@ -4689,11 +4684,7 @@ cmdputs(const char *s) | |||
4689 | str = "${#"; | 4684 | str = "${#"; |
4690 | else | 4685 | else |
4691 | str = "${"; | 4686 | str = "${"; |
4692 | if (!(subtype & VSQUOTE) == !(quoted & 1)) | 4687 | goto dostr; |
4693 | goto dostr; | ||
4694 | quoted ^= 1; | ||
4695 | c = '"'; | ||
4696 | break; | ||
4697 | case CTLENDVAR: | 4688 | case CTLENDVAR: |
4698 | str = "\"}" + !(quoted & 1); | 4689 | str = "\"}" + !(quoted & 1); |
4699 | quoted >>= 1; | 4690 | quoted >>= 1; |
@@ -4702,9 +4693,6 @@ cmdputs(const char *s) | |||
4702 | case CTLBACKQ: | 4693 | case CTLBACKQ: |
4703 | str = "$(...)"; | 4694 | str = "$(...)"; |
4704 | goto dostr; | 4695 | goto dostr; |
4705 | case CTLBACKQ+CTLQUOTE: | ||
4706 | str = "\"$(...)\""; | ||
4707 | goto dostr; | ||
4708 | #if ENABLE_SH_MATH_SUPPORT | 4696 | #if ENABLE_SH_MATH_SUPPORT |
4709 | case CTLARI: | 4697 | case CTLARI: |
4710 | str = "$(("; | 4698 | str = "$(("; |
@@ -5822,7 +5810,7 @@ ash_arith(const char *s) | |||
5822 | arith_t result; | 5810 | arith_t result; |
5823 | 5811 | ||
5824 | math_state.lookupvar = lookupvar; | 5812 | math_state.lookupvar = lookupvar; |
5825 | math_state.setvar = setvar2; | 5813 | math_state.setvar = setvar0; |
5826 | //math_state.endofname = endofname; | 5814 | //math_state.endofname = endofname; |
5827 | 5815 | ||
5828 | INT_OFF; | 5816 | INT_OFF; |
@@ -5843,18 +5831,23 @@ ash_arith(const char *s) | |||
5843 | #define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ | 5831 | #define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ |
5844 | #define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ | 5832 | #define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ |
5845 | #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ | 5833 | #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ |
5846 | #define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */ | 5834 | #define EXP_QPAT 0x20 /* pattern in quoted parameter expansion */ |
5847 | #define EXP_VARTILDE2 0x40 /* expand tildes after colons only */ | 5835 | #define EXP_VARTILDE2 0x40 /* expand tildes after colons only */ |
5848 | #define EXP_WORD 0x80 /* expand word in parameter expansion */ | 5836 | #define EXP_WORD 0x80 /* expand word in parameter expansion */ |
5849 | #define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */ | 5837 | #define EXP_QUOTED 0x100 /* expand word in double quotes */ |
5850 | /* | 5838 | /* |
5851 | * rmescape() flags | 5839 | * rmescape() flags |
5852 | */ | 5840 | */ |
5853 | #define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ | 5841 | #define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ |
5854 | #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ | 5842 | #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ |
5855 | #define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */ | ||
5856 | #define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */ | 5843 | #define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */ |
5857 | #define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ | 5844 | #define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ |
5845 | #define RMESCAPE_SLASH 0x20 /* Stop globbing after slash */ | ||
5846 | |||
5847 | /* Add CTLESC when necessary. */ | ||
5848 | #define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT | EXP_REDIR) | ||
5849 | /* Do not skip NUL characters. */ | ||
5850 | #define QUOTES_KEEPNUL EXP_TILDE | ||
5858 | 5851 | ||
5859 | /* | 5852 | /* |
5860 | * Structure specifying which parts of the string should be searched | 5853 | * Structure specifying which parts of the string should be searched |
@@ -5919,14 +5912,16 @@ esclen(const char *start, const char *p) | |||
5919 | static char * | 5912 | static char * |
5920 | rmescapes(char *str, int flag) | 5913 | rmescapes(char *str, int flag) |
5921 | { | 5914 | { |
5922 | static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' }; | 5915 | static const char qchars[] ALIGN1 = { |
5916 | IF_ASH_BASH_COMPAT('/',) CTLESC, CTLQUOTEMARK, '\0' }; | ||
5923 | 5917 | ||
5924 | char *p, *q, *r; | 5918 | char *p, *q, *r; |
5925 | unsigned inquotes; | 5919 | unsigned inquotes; |
5926 | unsigned protect_against_glob; | 5920 | unsigned protect_against_glob; |
5927 | unsigned globbing; | 5921 | unsigned globbing; |
5922 | IF_ASH_BASH_COMPAT(unsigned slash = flag & RMESCAPE_SLASH;) | ||
5928 | 5923 | ||
5929 | p = strpbrk(str, qchars); | 5924 | p = strpbrk(str, qchars IF_ASH_BASH_COMPAT(+ !slash)); |
5930 | if (!p) | 5925 | if (!p) |
5931 | return str; | 5926 | return str; |
5932 | 5927 | ||
@@ -5953,13 +5948,11 @@ rmescapes(char *str, int flag) | |||
5953 | } | 5948 | } |
5954 | } | 5949 | } |
5955 | 5950 | ||
5956 | inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED; | 5951 | inquotes = 0; |
5957 | globbing = flag & RMESCAPE_GLOB; | 5952 | globbing = flag & RMESCAPE_GLOB; |
5958 | protect_against_glob = globbing; | 5953 | protect_against_glob = globbing; |
5959 | while (*p) { | 5954 | while (*p) { |
5960 | if ((unsigned char)*p == CTLQUOTEMARK) { | 5955 | if ((unsigned char)*p == CTLQUOTEMARK) { |
5961 | // TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0 | ||
5962 | // (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok? | ||
5963 | // Note: both inquotes and protect_against_glob only affect whether | 5956 | // Note: both inquotes and protect_against_glob only affect whether |
5964 | // CTLESC,<ch> gets converted to <ch> or to \<ch> | 5957 | // CTLESC,<ch> gets converted to <ch> or to \<ch> |
5965 | inquotes = ~inquotes; | 5958 | inquotes = ~inquotes; |
@@ -5967,17 +5960,23 @@ rmescapes(char *str, int flag) | |||
5967 | protect_against_glob = globbing; | 5960 | protect_against_glob = globbing; |
5968 | continue; | 5961 | continue; |
5969 | } | 5962 | } |
5970 | if (*p == '\\') { | ||
5971 | /* naked back slash */ | ||
5972 | protect_against_glob = 0; | ||
5973 | goto copy; | ||
5974 | } | ||
5975 | if ((unsigned char)*p == CTLESC) { | 5963 | if ((unsigned char)*p == CTLESC) { |
5976 | p++; | 5964 | p++; |
5977 | if (protect_against_glob && inquotes && *p != '/') { | 5965 | if (protect_against_glob) { |
5978 | *q++ = '\\'; | 5966 | *q++ = '\\'; |
5979 | } | 5967 | } |
5968 | } else if (*p == '\\' && !inquotes) { | ||
5969 | /* naked back slash */ | ||
5970 | protect_against_glob = 0; | ||
5971 | goto copy; | ||
5972 | } | ||
5973 | #if ENABLE_ASH_BASH_COMPAT | ||
5974 | else if (*p == '/' && slash) { | ||
5975 | /* stop handling globbing and mark location of slash */ | ||
5976 | globbing = slash = 0; | ||
5977 | *p = CTLESC; | ||
5980 | } | 5978 | } |
5979 | #endif | ||
5981 | protect_against_glob = globbing; | 5980 | protect_against_glob = globbing; |
5982 | copy: | 5981 | copy: |
5983 | *q++ = *p++; | 5982 | *q++ = *p++; |
@@ -5997,13 +5996,9 @@ rmescapes(char *str, int flag) | |||
5997 | * Returns an stalloced string. | 5996 | * Returns an stalloced string. |
5998 | */ | 5997 | */ |
5999 | static char * | 5998 | static char * |
6000 | preglob(const char *pattern, int quoted, int flag) | 5999 | preglob(const char *pattern, int flag) |
6001 | { | 6000 | { |
6002 | flag |= RMESCAPE_GLOB; | 6001 | return rmescapes((char *)pattern, flag | RMESCAPE_GLOB); |
6003 | if (quoted) { | ||
6004 | flag |= RMESCAPE_QUOTED; | ||
6005 | } | ||
6006 | return rmescapes((char *)pattern, flag); | ||
6007 | } | 6002 | } |
6008 | 6003 | ||
6009 | /* | 6004 | /* |
@@ -6012,29 +6007,36 @@ preglob(const char *pattern, int quoted, int flag) | |||
6012 | static void | 6007 | static void |
6013 | memtodest(const char *p, size_t len, int syntax, int quotes) | 6008 | memtodest(const char *p, size_t len, int syntax, int quotes) |
6014 | { | 6009 | { |
6015 | char *q = expdest; | 6010 | char *q; |
6011 | |||
6012 | if (!len) | ||
6013 | return; | ||
6016 | 6014 | ||
6017 | q = makestrspace(quotes ? len * 2 : len, q); | 6015 | q = makestrspace((quotes & QUOTES_ESC) ? len * 2 : len, expdest); |
6018 | 6016 | ||
6019 | while (len--) { | 6017 | do { |
6020 | unsigned char c = *p++; | 6018 | unsigned char c = *p++; |
6021 | if (c == '\0') | 6019 | if (c) { |
6022 | continue; | ||
6023 | if (quotes) { | ||
6024 | int n = SIT(c, syntax); | 6020 | int n = SIT(c, syntax); |
6025 | if (n == CCTL || n == CBACK) | 6021 | if ((quotes & QUOTES_ESC) && |
6022 | ((n == CCTL) || | ||
6023 | (((quotes & EXP_FULL) || syntax != BASESYNTAX) && | ||
6024 | n == CBACK))) | ||
6026 | USTPUTC(CTLESC, q); | 6025 | USTPUTC(CTLESC, q); |
6027 | } | 6026 | } else if (!(quotes & QUOTES_KEEPNUL)) |
6027 | continue; | ||
6028 | USTPUTC(c, q); | 6028 | USTPUTC(c, q); |
6029 | } | 6029 | } while (--len); |
6030 | 6030 | ||
6031 | expdest = q; | 6031 | expdest = q; |
6032 | } | 6032 | } |
6033 | 6033 | ||
6034 | static void | 6034 | static size_t |
6035 | strtodest(const char *p, int syntax, int quotes) | 6035 | strtodest(const char *p, int syntax, int quotes) |
6036 | { | 6036 | { |
6037 | memtodest(p, strlen(p), syntax, quotes); | 6037 | size_t len = strlen(p); |
6038 | memtodest(p, len, syntax, quotes); | ||
6039 | return len; | ||
6038 | } | 6040 | } |
6039 | 6041 | ||
6040 | /* | 6042 | /* |
@@ -6107,7 +6109,7 @@ exptilde(char *startp, char *p, int flags) | |||
6107 | char *name; | 6109 | char *name; |
6108 | struct passwd *pw; | 6110 | struct passwd *pw; |
6109 | const char *home; | 6111 | const char *home; |
6110 | int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); | 6112 | int quotes = flags & QUOTES_ESC; |
6111 | 6113 | ||
6112 | name = p + 1; | 6114 | name = p + 1; |
6113 | 6115 | ||
@@ -6223,7 +6225,7 @@ evalbackcmd(union node *n, struct backcmd *result) | |||
6223 | * Expand stuff in backwards quotes. | 6225 | * Expand stuff in backwards quotes. |
6224 | */ | 6226 | */ |
6225 | static void | 6227 | static void |
6226 | expbackq(union node *cmd, int quoted, int quotes) | 6228 | expbackq(union node *cmd, int flag) |
6227 | { | 6229 | { |
6228 | struct backcmd in; | 6230 | struct backcmd in; |
6229 | int i; | 6231 | int i; |
@@ -6231,7 +6233,7 @@ expbackq(union node *cmd, int quoted, int quotes) | |||
6231 | char *p; | 6233 | char *p; |
6232 | char *dest; | 6234 | char *dest; |
6233 | int startloc; | 6235 | int startloc; |
6234 | int syntax = quoted ? DQSYNTAX : BASESYNTAX; | 6236 | int syntax = flag & EXP_QUOTED ? DQSYNTAX : BASESYNTAX; |
6235 | struct stackmark smark; | 6237 | struct stackmark smark; |
6236 | 6238 | ||
6237 | INT_OFF; | 6239 | INT_OFF; |
@@ -6247,11 +6249,11 @@ expbackq(union node *cmd, int quoted, int quotes) | |||
6247 | if (i == 0) | 6249 | if (i == 0) |
6248 | goto read; | 6250 | goto read; |
6249 | for (;;) { | 6251 | for (;;) { |
6250 | memtodest(p, i, syntax, quotes); | 6252 | memtodest(p, i, syntax, flag & QUOTES_ESC); |
6251 | read: | 6253 | read: |
6252 | if (in.fd < 0) | 6254 | if (in.fd < 0) |
6253 | break; | 6255 | break; |
6254 | i = nonblock_immune_read(in.fd, buf, sizeof(buf), /*loop_on_EINTR:*/ 1); | 6256 | i = nonblock_immune_read(in.fd, buf, sizeof(buf)); |
6255 | TRACE(("expbackq: read returns %d\n", i)); | 6257 | TRACE(("expbackq: read returns %d\n", i)); |
6256 | if (i <= 0) | 6258 | if (i <= 0) |
6257 | break; | 6259 | break; |
@@ -6272,7 +6274,7 @@ expbackq(union node *cmd, int quoted, int quotes) | |||
6272 | STUNPUTC(dest); | 6274 | STUNPUTC(dest); |
6273 | expdest = dest; | 6275 | expdest = dest; |
6274 | 6276 | ||
6275 | if (quoted == 0) | 6277 | if (!(flag & EXP_QUOTED)) |
6276 | recordregion(startloc, dest - (char *)stackblock(), 0); | 6278 | recordregion(startloc, dest - (char *)stackblock(), 0); |
6277 | TRACE(("evalbackq: size:%d:'%.*s'\n", | 6279 | TRACE(("evalbackq: size:%d:'%.*s'\n", |
6278 | (int)((dest - (char *)stackblock()) - startloc), | 6280 | (int)((dest - (char *)stackblock()) - startloc), |
@@ -6286,11 +6288,10 @@ expbackq(union node *cmd, int quoted, int quotes) | |||
6286 | * evaluate, place result in (backed up) result, adjust string position. | 6288 | * evaluate, place result in (backed up) result, adjust string position. |
6287 | */ | 6289 | */ |
6288 | static void | 6290 | static void |
6289 | expari(int quotes) | 6291 | expari(int flag) |
6290 | { | 6292 | { |
6291 | char *p, *start; | 6293 | char *p, *start; |
6292 | int begoff; | 6294 | int begoff; |
6293 | int flag; | ||
6294 | int len; | 6295 | int len; |
6295 | 6296 | ||
6296 | /* ifsfree(); */ | 6297 | /* ifsfree(); */ |
@@ -6328,16 +6329,14 @@ expari(int quotes) | |||
6328 | 6329 | ||
6329 | removerecordregions(begoff); | 6330 | removerecordregions(begoff); |
6330 | 6331 | ||
6331 | flag = p[1]; | ||
6332 | |||
6333 | expdest = p; | 6332 | expdest = p; |
6334 | 6333 | ||
6335 | if (quotes) | 6334 | if (flag & QUOTES_ESC) |
6336 | rmescapes(p + 2, 0); | 6335 | rmescapes(p + 1, 0); |
6337 | 6336 | ||
6338 | len = cvtnum(ash_arith(p + 2)); | 6337 | len = cvtnum(ash_arith(p + 1)); |
6339 | 6338 | ||
6340 | if (flag != '"') | 6339 | if (!(flag & EXP_QUOTED)) |
6341 | recordregion(begoff, begoff + len, 0); | 6340 | recordregion(begoff, begoff + len, 0); |
6342 | } | 6341 | } |
6343 | #endif | 6342 | #endif |
@@ -6365,15 +6364,13 @@ argstr(char *p, int flags, struct strlist *var_str_list) | |||
6365 | CTLESC, | 6364 | CTLESC, |
6366 | CTLVAR, | 6365 | CTLVAR, |
6367 | CTLBACKQ, | 6366 | CTLBACKQ, |
6368 | CTLBACKQ | CTLQUOTE, | ||
6369 | #if ENABLE_SH_MATH_SUPPORT | 6367 | #if ENABLE_SH_MATH_SUPPORT |
6370 | CTLENDARI, | 6368 | CTLENDARI, |
6371 | #endif | 6369 | #endif |
6372 | '\0' | 6370 | '\0' |
6373 | }; | 6371 | }; |
6374 | const char *reject = spclchars; | 6372 | const char *reject = spclchars; |
6375 | int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ | 6373 | int breakall = (flags & (EXP_WORD | EXP_QUOTED)) == EXP_WORD; |
6376 | int breakall = flags & EXP_WORD; | ||
6377 | int inquotes; | 6374 | int inquotes; |
6378 | size_t length; | 6375 | size_t length; |
6379 | int startloc; | 6376 | int startloc; |
@@ -6391,8 +6388,6 @@ argstr(char *p, int flags, struct strlist *var_str_list) | |||
6391 | flags &= ~EXP_TILDE; | 6388 | flags &= ~EXP_TILDE; |
6392 | tilde: | 6389 | tilde: |
6393 | q = p; | 6390 | q = p; |
6394 | if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD)) | ||
6395 | q++; | ||
6396 | if (*q == '~') | 6391 | if (*q == '~') |
6397 | p = exptilde(p, q, flags); | 6392 | p = exptilde(p, q, flags); |
6398 | } | 6393 | } |
@@ -6449,19 +6444,14 @@ argstr(char *p, int flags, struct strlist *var_str_list) | |||
6449 | case CTLENDVAR: /* ??? */ | 6444 | case CTLENDVAR: /* ??? */ |
6450 | goto breakloop; | 6445 | goto breakloop; |
6451 | case CTLQUOTEMARK: | 6446 | case CTLQUOTEMARK: |
6447 | inquotes ^= EXP_QUOTED; | ||
6452 | /* "$@" syntax adherence hack */ | 6448 | /* "$@" syntax adherence hack */ |
6453 | if (!inquotes | 6449 | if (inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) { |
6454 | && memcmp(p, dolatstr, 4) == 0 | 6450 | p = evalvar(p + 1, flags | inquotes, /* var_str_list: */ NULL) + 1; |
6455 | && ( p[4] == (char)CTLQUOTEMARK | ||
6456 | || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK) | ||
6457 | ) | ||
6458 | ) { | ||
6459 | p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1; | ||
6460 | goto start; | 6451 | goto start; |
6461 | } | 6452 | } |
6462 | inquotes = !inquotes; | ||
6463 | addquote: | 6453 | addquote: |
6464 | if (quotes) { | 6454 | if (flags & QUOTES_ESC) { |
6465 | p--; | 6455 | p--; |
6466 | length++; | 6456 | length++; |
6467 | startloc++; | 6457 | startloc++; |
@@ -6470,22 +6460,30 @@ argstr(char *p, int flags, struct strlist *var_str_list) | |||
6470 | case CTLESC: | 6460 | case CTLESC: |
6471 | startloc++; | 6461 | startloc++; |
6472 | length++; | 6462 | length++; |
6463 | |||
6464 | /* | ||
6465 | * Quoted parameter expansion pattern: remove quote | ||
6466 | * unless inside inner quotes or we have a literal | ||
6467 | * backslash. | ||
6468 | */ | ||
6469 | if (((flags | inquotes) & (EXP_QPAT | EXP_QUOTED)) == | ||
6470 | EXP_QPAT && *p != '\\') | ||
6471 | break; | ||
6472 | |||
6473 | goto addquote; | 6473 | goto addquote; |
6474 | case CTLVAR: | 6474 | case CTLVAR: |
6475 | TRACE(("argstr: evalvar('%s')\n", p)); | 6475 | TRACE(("argstr: evalvar('%s')\n", p)); |
6476 | p = evalvar(p, flags, var_str_list); | 6476 | p = evalvar(p, flags | inquotes, var_str_list); |
6477 | TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock())); | 6477 | TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock())); |
6478 | goto start; | 6478 | goto start; |
6479 | case CTLBACKQ: | 6479 | case CTLBACKQ: |
6480 | c = '\0'; | 6480 | expbackq(argbackq->n, flags | inquotes); |
6481 | case CTLBACKQ|CTLQUOTE: | ||
6482 | expbackq(argbackq->n, c, quotes); | ||
6483 | argbackq = argbackq->next; | 6481 | argbackq = argbackq->next; |
6484 | goto start; | 6482 | goto start; |
6485 | #if ENABLE_SH_MATH_SUPPORT | 6483 | #if ENABLE_SH_MATH_SUPPORT |
6486 | case CTLENDARI: | 6484 | case CTLENDARI: |
6487 | p--; | 6485 | p--; |
6488 | expari(quotes); | 6486 | expari(flags | inquotes); |
6489 | goto start; | 6487 | goto start; |
6490 | #endif | 6488 | #endif |
6491 | } | 6489 | } |
@@ -6615,60 +6613,17 @@ varunset(const char *end, const char *var, const char *umsg, int varflags) | |||
6615 | ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail); | 6613 | ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail); |
6616 | } | 6614 | } |
6617 | 6615 | ||
6618 | #if ENABLE_ASH_BASH_COMPAT | ||
6619 | static char * | ||
6620 | parse_sub_pattern(char *arg, int varflags) | ||
6621 | { | ||
6622 | char *idx, *repl = NULL; | ||
6623 | unsigned char c; | ||
6624 | |||
6625 | //char *org_arg = arg; | ||
6626 | //bb_error_msg("arg:'%s' varflags:%x", arg, varflags); | ||
6627 | idx = arg; | ||
6628 | while (1) { | ||
6629 | c = *arg; | ||
6630 | if (!c) | ||
6631 | break; | ||
6632 | if (c == '/') { | ||
6633 | /* Only the first '/' seen is our separator */ | ||
6634 | if (!repl) { | ||
6635 | repl = idx + 1; | ||
6636 | c = '\0'; | ||
6637 | } | ||
6638 | } | ||
6639 | *idx++ = c; | ||
6640 | arg++; | ||
6641 | /* | ||
6642 | * Example: v='ab\c'; echo ${v/\\b/_\\_\z_} | ||
6643 | * The result is a_\_z_c (not a\_\_z_c)! | ||
6644 | * | ||
6645 | * Enable debug prints in this function and you'll see: | ||
6646 | * ash: arg:'\\b/_\\_z_' varflags:d | ||
6647 | * ash: pattern:'\\b' repl:'_\_z_' | ||
6648 | * That is, \\b is interpreted as \\b, but \\_ as \_! | ||
6649 | * IOW: search pattern and replace string treat backslashes | ||
6650 | * differently! That is the reason why we check repl below: | ||
6651 | */ | ||
6652 | if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE)) | ||
6653 | arg++; /* skip both '\', not just first one */ | ||
6654 | } | ||
6655 | *idx = c; /* NUL */ | ||
6656 | //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl); | ||
6657 | |||
6658 | return repl; | ||
6659 | } | ||
6660 | #endif /* ENABLE_ASH_BASH_COMPAT */ | ||
6661 | |||
6662 | static const char * | 6616 | static const char * |
6663 | subevalvar(char *p, char *varname, int strloc, int subtype, | 6617 | subevalvar(char *p, char *varname, int strloc, int subtype, |
6664 | int startloc, int varflags, int quotes, struct strlist *var_str_list) | 6618 | int startloc, int varflags, int flag, struct strlist *var_str_list) |
6665 | { | 6619 | { |
6666 | struct nodelist *saveargbackq = argbackq; | 6620 | struct nodelist *saveargbackq = argbackq; |
6621 | int quotes = flag & QUOTES_ESC; | ||
6667 | char *startp; | 6622 | char *startp; |
6668 | char *loc; | 6623 | char *loc; |
6669 | char *rmesc, *rmescend; | 6624 | char *rmesc, *rmescend; |
6670 | char *str; | 6625 | char *str; |
6671 | IF_ASH_BASH_COMPAT(const char *repl = NULL;) | 6626 | IF_ASH_BASH_COMPAT(char *repl = NULL;) |
6672 | IF_ASH_BASH_COMPAT(int pos, len, orig_len;) | 6627 | IF_ASH_BASH_COMPAT(int pos, len, orig_len;) |
6673 | int saveherefd = herefd; | 6628 | int saveherefd = herefd; |
6674 | int amount, resetloc; | 6629 | int amount, resetloc; |
@@ -6680,7 +6635,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
6680 | // p, varname, strloc, subtype, startloc, varflags, quotes); | 6635 | // p, varname, strloc, subtype, startloc, varflags, quotes); |
6681 | 6636 | ||
6682 | herefd = -1; | 6637 | herefd = -1; |
6683 | argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0, | 6638 | argstr(p, EXP_TILDE | (subtype != VSASSIGN && subtype != VSQUESTION ? |
6639 | (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE) : 0), | ||
6684 | var_str_list); | 6640 | var_str_list); |
6685 | STPUTC('\0', expdest); | 6641 | STPUTC('\0', expdest); |
6686 | herefd = saveherefd; | 6642 | herefd = saveherefd; |
@@ -6689,7 +6645,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
6689 | 6645 | ||
6690 | switch (subtype) { | 6646 | switch (subtype) { |
6691 | case VSASSIGN: | 6647 | case VSASSIGN: |
6692 | setvar2(varname, startp); | 6648 | setvar0(varname, startp); |
6693 | amount = startp - expdest; | 6649 | amount = startp - expdest; |
6694 | STADJUST(amount, expdest); | 6650 | STADJUST(amount, expdest); |
6695 | return startp; | 6651 | return startp; |
@@ -6792,7 +6748,17 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
6792 | } | 6748 | } |
6793 | rmescend--; | 6749 | rmescend--; |
6794 | str = (char *)stackblock() + strloc; | 6750 | str = (char *)stackblock() + strloc; |
6795 | preglob(str, varflags & VSQUOTE, 0); | 6751 | /* |
6752 | * Example: v='a\bc'; echo ${v/\\b/_\\_\z_} | ||
6753 | * The result is a_\_z_c (not a\_\_z_c)! | ||
6754 | * | ||
6755 | * The search pattern and replace string treat backslashes differently! | ||
6756 | * RMESCAPE_SLASH causes preglob to work differently on the pattern | ||
6757 | * and string. It's only used on the first call. | ||
6758 | */ | ||
6759 | preglob(str, IF_ASH_BASH_COMPAT( | ||
6760 | (subtype == VSREPLACE || subtype == VSREPLACEALL) && !repl ? | ||
6761 | RMESCAPE_SLASH :) 0); | ||
6796 | 6762 | ||
6797 | #if ENABLE_ASH_BASH_COMPAT | 6763 | #if ENABLE_ASH_BASH_COMPAT |
6798 | workloc = expdest - (char *)stackblock(); | 6764 | workloc = expdest - (char *)stackblock(); |
@@ -6800,11 +6766,12 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
6800 | char *idx, *end; | 6766 | char *idx, *end; |
6801 | 6767 | ||
6802 | if (!repl) { | 6768 | if (!repl) { |
6803 | repl = parse_sub_pattern(str, varflags); | 6769 | if ((repl=strchr(str, CTLESC))) |
6804 | //bb_error_msg("repl:'%s'", repl); | 6770 | *repl++ = '\0'; |
6805 | if (!repl) | 6771 | else |
6806 | repl = nullstr; | 6772 | repl = nullstr; |
6807 | } | 6773 | } |
6774 | //bb_error_msg("str:'%s' repl:'%s'", str, repl); | ||
6808 | 6775 | ||
6809 | /* If there's no pattern to match, return the expansion unmolested */ | 6776 | /* If there's no pattern to match, return the expansion unmolested */ |
6810 | if (str[0] == '\0') | 6777 | if (str[0] == '\0') |
@@ -6937,13 +6904,16 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
6937 | const char *p; | 6904 | const char *p; |
6938 | int num; | 6905 | int num; |
6939 | int i; | 6906 | int i; |
6940 | int sepq = 0; | ||
6941 | ssize_t len = 0; | 6907 | ssize_t len = 0; |
6908 | int sep; | ||
6909 | int quoted = flags & EXP_QUOTED; | ||
6942 | int subtype = varflags & VSTYPE; | 6910 | int subtype = varflags & VSTYPE; |
6943 | int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); | 6911 | int discard = subtype == VSPLUS || subtype == VSLENGTH; |
6944 | int quoted = varflags & VSQUOTE; | 6912 | int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; |
6945 | int syntax = quoted ? DQSYNTAX : BASESYNTAX; | 6913 | int syntax = quoted ? DQSYNTAX : BASESYNTAX; |
6946 | 6914 | ||
6915 | sep = quoted ? ((flags & EXP_FULL) << CHAR_BIT) : 0; | ||
6916 | |||
6947 | switch (*name) { | 6917 | switch (*name) { |
6948 | case '$': | 6918 | case '$': |
6949 | num = rootpid; | 6919 | num = rootpid; |
@@ -6978,7 +6948,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
6978 | break; | 6948 | break; |
6979 | case '@': { | 6949 | case '@': { |
6980 | char **ap; | 6950 | char **ap; |
6981 | int sep; | 6951 | char sepc; |
6982 | 6952 | ||
6983 | if (quoted && (flags & EXP_FULL)) { | 6953 | if (quoted && (flags & EXP_FULL)) { |
6984 | /* note: this is not meant as PEOF value */ | 6954 | /* note: this is not meant as PEOF value */ |
@@ -6988,39 +6958,20 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
6988 | /* fall through */ | 6958 | /* fall through */ |
6989 | case '*': | 6959 | case '*': |
6990 | sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' '; | 6960 | sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' '; |
6991 | i = SIT(sep, syntax); | ||
6992 | if (quotes && (i == CCTL || i == CBACK)) | ||
6993 | sepq = 1; | ||
6994 | param: | 6961 | param: |
6995 | ap = shellparam.p; | 6962 | ap = shellparam.p; |
6963 | sepc = sep; | ||
6996 | if (!ap) | 6964 | if (!ap) |
6997 | return -1; | 6965 | return -1; |
6998 | while ((p = *ap++) != NULL) { | 6966 | while ((p = *ap++) != NULL) { |
6999 | size_t partlen; | 6967 | len += strtodest(p, syntax, quotes); |
7000 | |||
7001 | partlen = strlen(p); | ||
7002 | len += partlen; | ||
7003 | |||
7004 | if (!(subtype == VSPLUS || subtype == VSLENGTH)) | ||
7005 | memtodest(p, partlen, syntax, quotes); | ||
7006 | 6968 | ||
7007 | if (*ap && sep) { | 6969 | if (*ap && sep) { |
7008 | char *q; | ||
7009 | |||
7010 | len++; | 6970 | len++; |
7011 | if (subtype == VSPLUS || subtype == VSLENGTH) { | 6971 | memtodest(&sepc, 1, syntax, quotes); |
7012 | continue; | ||
7013 | } | ||
7014 | q = expdest; | ||
7015 | if (sepq) | ||
7016 | STPUTC(CTLESC, q); | ||
7017 | /* note: may put NUL despite sep != 0 | ||
7018 | * (see sep = 1 << CHAR_BIT above) */ | ||
7019 | STPUTC(sep, q); | ||
7020 | expdest = q; | ||
7021 | } | 6972 | } |
7022 | } | 6973 | } |
7023 | return len; | 6974 | break; |
7024 | } /* case '@' and '*' */ | 6975 | } /* case '@' and '*' */ |
7025 | case '0': | 6976 | case '0': |
7026 | case '1': | 6977 | case '1': |
@@ -7069,9 +7020,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
7069 | if (!p) | 7020 | if (!p) |
7070 | return -1; | 7021 | return -1; |
7071 | 7022 | ||
7072 | len = strlen(p); | 7023 | len = strtodest(p, syntax, quotes); |
7073 | if (!(subtype == VSPLUS || subtype == VSLENGTH)) | ||
7074 | memtodest(p, len, syntax, quotes); | ||
7075 | #if ENABLE_UNICODE_SUPPORT | 7024 | #if ENABLE_UNICODE_SUPPORT |
7076 | if (subtype == VSLENGTH && len > 0) { | 7025 | if (subtype == VSLENGTH && len > 0) { |
7077 | reinit_unicode_for_ash(); | 7026 | reinit_unicode_for_ash(); |
@@ -7080,10 +7029,10 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
7080 | } | 7029 | } |
7081 | } | 7030 | } |
7082 | #endif | 7031 | #endif |
7083 | return len; | 7032 | break; |
7084 | } | 7033 | } |
7085 | 7034 | ||
7086 | if (subtype == VSPLUS || subtype == VSLENGTH) | 7035 | if (discard) |
7087 | STADJUST(-len, expdest); | 7036 | STADJUST(-len, expdest); |
7088 | return len; | 7037 | return len; |
7089 | } | 7038 | } |
@@ -7097,7 +7046,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
7097 | { | 7046 | { |
7098 | char varflags; | 7047 | char varflags; |
7099 | char subtype; | 7048 | char subtype; |
7100 | char quoted; | 7049 | int quoted; |
7101 | char easy; | 7050 | char easy; |
7102 | char *var; | 7051 | char *var; |
7103 | int patloc; | 7052 | int patloc; |
@@ -7106,7 +7055,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
7106 | 7055 | ||
7107 | varflags = (unsigned char) *p++; | 7056 | varflags = (unsigned char) *p++; |
7108 | subtype = varflags & VSTYPE; | 7057 | subtype = varflags & VSTYPE; |
7109 | quoted = varflags & VSQUOTE; | 7058 | quoted = flags & EXP_QUOTED; |
7110 | var = p; | 7059 | var = p; |
7111 | easy = (!quoted || (*var == '@' && shellparam.nparam)); | 7060 | easy = (!quoted || (*var == '@' && shellparam.nparam)); |
7112 | startloc = expdest - (char *)stackblock(); | 7061 | startloc = expdest - (char *)stackblock(); |
@@ -7127,7 +7076,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
7127 | if (varlen < 0) { | 7076 | if (varlen < 0) { |
7128 | argstr( | 7077 | argstr( |
7129 | p, | 7078 | p, |
7130 | flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD), | 7079 | flags | EXP_TILDE | EXP_WORD, |
7131 | var_str_list | 7080 | var_str_list |
7132 | ); | 7081 | ); |
7133 | goto end; | 7082 | goto end; |
@@ -7141,7 +7090,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
7141 | if (varlen < 0) { | 7090 | if (varlen < 0) { |
7142 | if (subevalvar(p, var, /* strloc: */ 0, | 7091 | if (subevalvar(p, var, /* strloc: */ 0, |
7143 | subtype, startloc, varflags, | 7092 | subtype, startloc, varflags, |
7144 | /* quotes: */ 0, | 7093 | /* quotes: */ flags & ~QUOTES_ESC, |
7145 | var_str_list) | 7094 | var_str_list) |
7146 | ) { | 7095 | ) { |
7147 | varflags &= ~VSNUL; | 7096 | varflags &= ~VSNUL; |
@@ -7198,10 +7147,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
7198 | STPUTC('\0', expdest); | 7147 | STPUTC('\0', expdest); |
7199 | patloc = expdest - (char *)stackblock(); | 7148 | patloc = expdest - (char *)stackblock(); |
7200 | if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype, | 7149 | if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype, |
7201 | startloc, varflags, | 7150 | startloc, varflags, flags, var_str_list)) { |
7202 | /* quotes: */ flags & (EXP_FULL | EXP_CASE | EXP_REDIR), | ||
7203 | var_str_list) | ||
7204 | ) { | ||
7205 | int amount = expdest - ( | 7151 | int amount = expdest - ( |
7206 | (char *)stackblock() + patloc - 1 | 7152 | (char *)stackblock() + patloc - 1 |
7207 | ); | 7153 | ); |
@@ -7220,7 +7166,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
7220 | unsigned char c = *p++; | 7166 | unsigned char c = *p++; |
7221 | if (c == CTLESC) | 7167 | if (c == CTLESC) |
7222 | p++; | 7168 | p++; |
7223 | else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { | 7169 | else if (c == CTLBACKQ) { |
7224 | if (varlen >= 0) | 7170 | if (varlen >= 0) |
7225 | argbackq = argbackq->next; | 7171 | argbackq = argbackq->next; |
7226 | } else if (c == CTLVAR) { | 7172 | } else if (c == CTLVAR) { |
@@ -7556,7 +7502,7 @@ expandmeta(struct strlist *str /*, int flag*/) | |||
7556 | savelastp = exparg.lastp; | 7502 | savelastp = exparg.lastp; |
7557 | 7503 | ||
7558 | INT_OFF; | 7504 | INT_OFF; |
7559 | p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP); | 7505 | p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); |
7560 | { | 7506 | { |
7561 | int i = strlen(str->text); | 7507 | int i = strlen(str->text); |
7562 | expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ | 7508 | expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ |
@@ -7646,7 +7592,7 @@ static void | |||
7646 | expandhere(union node *arg, int fd) | 7592 | expandhere(union node *arg, int fd) |
7647 | { | 7593 | { |
7648 | herefd = fd; | 7594 | herefd = fd; |
7649 | expandarg(arg, (struct arglist *)NULL, 0); | 7595 | expandarg(arg, (struct arglist *)NULL, EXP_QUOTED); |
7650 | full_write(fd, stackblock(), expdest - (char *)stackblock()); | 7596 | full_write(fd, stackblock(), expdest - (char *)stackblock()); |
7651 | } | 7597 | } |
7652 | 7598 | ||
@@ -7656,7 +7602,7 @@ expandhere(union node *arg, int fd) | |||
7656 | static int | 7602 | static int |
7657 | patmatch(char *pattern, const char *string) | 7603 | patmatch(char *pattern, const char *string) |
7658 | { | 7604 | { |
7659 | return pmatch(preglob(pattern, 0, 0), string); | 7605 | return pmatch(preglob(pattern, 0), string); |
7660 | } | 7606 | } |
7661 | 7607 | ||
7662 | /* | 7608 | /* |
@@ -8956,7 +8902,7 @@ evalfor(union node *n, int flags) | |||
8956 | arglist.list = NULL; | 8902 | arglist.list = NULL; |
8957 | arglist.lastp = &arglist.list; | 8903 | arglist.lastp = &arglist.list; |
8958 | for (argp = n->nfor.args; argp; argp = argp->narg.next) { | 8904 | for (argp = n->nfor.args; argp; argp = argp->narg.next) { |
8959 | expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD); | 8905 | expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); |
8960 | /* XXX */ | 8906 | /* XXX */ |
8961 | if (evalskip) | 8907 | if (evalskip) |
8962 | goto out; | 8908 | goto out; |
@@ -8967,7 +8913,7 @@ evalfor(union node *n, int flags) | |||
8967 | loopnest++; | 8913 | loopnest++; |
8968 | flags &= EV_TESTED; | 8914 | flags &= EV_TESTED; |
8969 | for (sp = arglist.list; sp; sp = sp->next) { | 8915 | for (sp = arglist.list; sp; sp = sp->next) { |
8970 | setvar2(n->nfor.var, sp->text); | 8916 | setvar0(n->nfor.var, sp->text); |
8971 | evaltree(n->nfor.body, flags); | 8917 | evaltree(n->nfor.body, flags); |
8972 | if (evalskip) { | 8918 | if (evalskip) { |
8973 | if (evalskip == SKIPCONT && --skipcount <= 0) { | 8919 | if (evalskip == SKIPCONT && --skipcount <= 0) { |
@@ -9362,7 +9308,8 @@ parse_command_args(char **argv, const char **path) | |||
9362 | * Make a variable a local variable. When a variable is made local, it's | 9308 | * Make a variable a local variable. When a variable is made local, it's |
9363 | * value and flags are saved in a localvar structure. The saved values | 9309 | * value and flags are saved in a localvar structure. The saved values |
9364 | * will be restored when the shell function returns. We handle the name | 9310 | * will be restored when the shell function returns. We handle the name |
9365 | * "-" as a special case. | 9311 | * "-" as a special case: it makes changes to "set +-options" local |
9312 | * (options will be restored on return from the function). | ||
9366 | */ | 9313 | */ |
9367 | static void | 9314 | static void |
9368 | mklocal(char *name) | 9315 | mklocal(char *name) |
@@ -9370,21 +9317,37 @@ mklocal(char *name) | |||
9370 | struct localvar *lvp; | 9317 | struct localvar *lvp; |
9371 | struct var **vpp; | 9318 | struct var **vpp; |
9372 | struct var *vp; | 9319 | struct var *vp; |
9320 | char *eq = strchr(name, '='); | ||
9373 | 9321 | ||
9374 | INT_OFF; | 9322 | INT_OFF; |
9375 | lvp = ckzalloc(sizeof(struct localvar)); | 9323 | /* Cater for duplicate "local". Examples: |
9324 | * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x | ||
9325 | * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x | ||
9326 | */ | ||
9327 | lvp = localvars; | ||
9328 | while (lvp) { | ||
9329 | if (lvp->vp && varcmp(lvp->vp->var_text, name) == 0) { | ||
9330 | if (eq) | ||
9331 | setvareq(name, 0); | ||
9332 | /* else: | ||
9333 | * it's a duplicate "local VAR" declaration, do nothing | ||
9334 | */ | ||
9335 | return; | ||
9336 | } | ||
9337 | lvp = lvp->next; | ||
9338 | } | ||
9339 | |||
9340 | lvp = ckzalloc(sizeof(*lvp)); | ||
9376 | if (LONE_DASH(name)) { | 9341 | if (LONE_DASH(name)) { |
9377 | char *p; | 9342 | char *p; |
9378 | p = ckmalloc(sizeof(optlist)); | 9343 | p = ckmalloc(sizeof(optlist)); |
9379 | lvp->text = memcpy(p, optlist, sizeof(optlist)); | 9344 | lvp->text = memcpy(p, optlist, sizeof(optlist)); |
9380 | vp = NULL; | 9345 | vp = NULL; |
9381 | } else { | 9346 | } else { |
9382 | char *eq; | ||
9383 | |||
9384 | vpp = hashvar(name); | 9347 | vpp = hashvar(name); |
9385 | vp = *findvar(vpp, name); | 9348 | vp = *findvar(vpp, name); |
9386 | eq = strchr(name, '='); | ||
9387 | if (vp == NULL) { | 9349 | if (vp == NULL) { |
9350 | /* variable did not exist yet */ | ||
9388 | if (eq) | 9351 | if (eq) |
9389 | setvareq(name, VSTRFIXED); | 9352 | setvareq(name, VSTRFIXED); |
9390 | else | 9353 | else |
@@ -9394,12 +9357,15 @@ mklocal(char *name) | |||
9394 | } else { | 9357 | } else { |
9395 | lvp->text = vp->var_text; | 9358 | lvp->text = vp->var_text; |
9396 | lvp->flags = vp->flags; | 9359 | lvp->flags = vp->flags; |
9360 | /* make sure neither "struct var" nor string gets freed | ||
9361 | * during (un)setting: | ||
9362 | */ | ||
9397 | vp->flags |= VSTRFIXED|VTEXTFIXED; | 9363 | vp->flags |= VSTRFIXED|VTEXTFIXED; |
9398 | if (eq) | 9364 | if (eq) |
9399 | setvareq(name, 0); | 9365 | setvareq(name, 0); |
9400 | else | 9366 | else |
9401 | /* "local VAR" unsets VAR: */ | 9367 | /* "local VAR" unsets VAR: */ |
9402 | setvar(name, NULL, 0); | 9368 | setvar0(name, NULL); |
9403 | } | 9369 | } |
9404 | } | 9370 | } |
9405 | lvp->vp = vp; | 9371 | lvp->vp = vp; |
@@ -9914,7 +9880,7 @@ evalcommand(union node *cmd, int flags) | |||
9914 | * '_' in 'vi' command mode during line editing... | 9880 | * '_' in 'vi' command mode during line editing... |
9915 | * However I implemented that within libedit itself. | 9881 | * However I implemented that within libedit itself. |
9916 | */ | 9882 | */ |
9917 | setvar2("_", lastarg); | 9883 | setvar0("_", lastarg); |
9918 | } | 9884 | } |
9919 | popstackmark(&smark); | 9885 | popstackmark(&smark); |
9920 | } | 9886 | } |
@@ -10100,7 +10066,7 @@ preadfd(void) | |||
10100 | #if ENABLE_FEATURE_EDITING | 10066 | #if ENABLE_FEATURE_EDITING |
10101 | retry: | 10067 | retry: |
10102 | if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) | 10068 | if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) |
10103 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1); | 10069 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); |
10104 | else { | 10070 | else { |
10105 | int timeout = -1; | 10071 | int timeout = -1; |
10106 | # if ENABLE_ASH_IDLE_TIMEOUT | 10072 | # if ENABLE_ASH_IDLE_TIMEOUT |
@@ -10142,7 +10108,7 @@ preadfd(void) | |||
10142 | } | 10108 | } |
10143 | } | 10109 | } |
10144 | #else | 10110 | #else |
10145 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1); | 10111 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); |
10146 | #endif | 10112 | #endif |
10147 | 10113 | ||
10148 | #if 0 /* disabled: nonblock_immune_read() handles this problem */ | 10114 | #if 0 /* disabled: nonblock_immune_read() handles this problem */ |
@@ -11675,11 +11641,9 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
11675 | && c != '$' | 11641 | && c != '$' |
11676 | && (c != '"' || eofmark != NULL) | 11642 | && (c != '"' || eofmark != NULL) |
11677 | ) { | 11643 | ) { |
11678 | USTPUTC(CTLESC, out); | ||
11679 | USTPUTC('\\', out); | 11644 | USTPUTC('\\', out); |
11680 | } | 11645 | } |
11681 | if (SIT(c, SQSYNTAX) == CCTL) | 11646 | USTPUTC(CTLESC, out); |
11682 | USTPUTC(CTLESC, out); | ||
11683 | USTPUTC(c, out); | 11647 | USTPUTC(c, out); |
11684 | quotef = 1; | 11648 | quotef = 1; |
11685 | } | 11649 | } |
@@ -11697,9 +11661,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
11697 | goto quotemark; | 11661 | goto quotemark; |
11698 | case CENDQUOTE: | 11662 | case CENDQUOTE: |
11699 | IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;) | 11663 | IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;) |
11700 | if (eofmark != NULL && arinest == 0 | 11664 | if (eofmark != NULL && varnest == 0) { |
11701 | && varnest == 0 | ||
11702 | ) { | ||
11703 | USTPUTC(c, out); | 11665 | USTPUTC(c, out); |
11704 | } else { | 11666 | } else { |
11705 | if (dqvarnest == 0) { | 11667 | if (dqvarnest == 0) { |
@@ -11733,10 +11695,9 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
11733 | parenlevel--; | 11695 | parenlevel--; |
11734 | } else { | 11696 | } else { |
11735 | if (pgetc() == ')') { | 11697 | if (pgetc() == ')') { |
11698 | c = CTLENDARI; | ||
11736 | if (--arinest == 0) { | 11699 | if (--arinest == 0) { |
11737 | syntax = prevsyntax; | 11700 | syntax = prevsyntax; |
11738 | dblquote = (syntax == DQSYNTAX); | ||
11739 | c = CTLENDARI; | ||
11740 | } | 11701 | } |
11741 | } else { | 11702 | } else { |
11742 | /* | 11703 | /* |
@@ -12055,12 +12016,10 @@ parsesub: { | |||
12055 | do_pungetc: | 12016 | do_pungetc: |
12056 | pungetc(); | 12017 | pungetc(); |
12057 | } | 12018 | } |
12058 | if (dblquote || arinest) | ||
12059 | flags |= VSQUOTE; | ||
12060 | ((unsigned char *)stackblock())[typeloc] = subtype | flags; | 12019 | ((unsigned char *)stackblock())[typeloc] = subtype | flags; |
12061 | if (subtype != VSNORMAL) { | 12020 | if (subtype != VSNORMAL) { |
12062 | varnest++; | 12021 | varnest++; |
12063 | if (dblquote || arinest) { | 12022 | if (dblquote) { |
12064 | dqvarnest++; | 12023 | dqvarnest++; |
12065 | } | 12024 | } |
12066 | } | 12025 | } |
@@ -12210,10 +12169,7 @@ parsebackq: { | |||
12210 | } | 12169 | } |
12211 | parsebackquote = savepbq; | 12170 | parsebackquote = savepbq; |
12212 | exception_handler = savehandler; | 12171 | exception_handler = savehandler; |
12213 | if (arinest || dblquote) | 12172 | USTPUTC(CTLBACKQ, out); |
12214 | USTPUTC(CTLBACKQ | CTLQUOTE, out); | ||
12215 | else | ||
12216 | USTPUTC(CTLBACKQ, out); | ||
12217 | if (oldstyle) | 12173 | if (oldstyle) |
12218 | goto parsebackq_oldreturn; | 12174 | goto parsebackq_oldreturn; |
12219 | goto parsebackq_newreturn; | 12175 | goto parsebackq_newreturn; |
@@ -12227,18 +12183,8 @@ parsearith: { | |||
12227 | if (++arinest == 1) { | 12183 | if (++arinest == 1) { |
12228 | prevsyntax = syntax; | 12184 | prevsyntax = syntax; |
12229 | syntax = ARISYNTAX; | 12185 | syntax = ARISYNTAX; |
12230 | USTPUTC(CTLARI, out); | ||
12231 | if (dblquote) | ||
12232 | USTPUTC('"', out); | ||
12233 | else | ||
12234 | USTPUTC(' ', out); | ||
12235 | } else { | ||
12236 | /* | ||
12237 | * we collapse embedded arithmetic expansion to | ||
12238 | * parenthesis, which should be equivalent | ||
12239 | */ | ||
12240 | USTPUTC('(', out); | ||
12241 | } | 12186 | } |
12187 | USTPUTC(CTLARI, out); | ||
12242 | goto parsearith_return; | 12188 | goto parsearith_return; |
12243 | } | 12189 | } |
12244 | #endif | 12190 | #endif |
@@ -12553,7 +12499,7 @@ expandstr(const char *ps) | |||
12553 | n.narg.text = wordtext; | 12499 | n.narg.text = wordtext; |
12554 | n.narg.backquote = backquotelist; | 12500 | n.narg.backquote = backquotelist; |
12555 | 12501 | ||
12556 | expandarg(&n, NULL, 0); | 12502 | expandarg(&n, NULL, EXP_QUOTED); |
12557 | return stackblock(); | 12503 | return stackblock(); |
12558 | } | 12504 | } |
12559 | #endif | 12505 | #endif |
@@ -13348,7 +13294,7 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
13348 | * to jump out of it. | 13294 | * to jump out of it. |
13349 | */ | 13295 | */ |
13350 | INT_OFF; | 13296 | INT_OFF; |
13351 | r = shell_builtin_read(setvar2, | 13297 | r = shell_builtin_read(setvar0, |
13352 | argptr, | 13298 | argptr, |
13353 | bltinlookup("IFS"), /* can be NULL */ | 13299 | bltinlookup("IFS"), /* can be NULL */ |
13354 | read_flags, | 13300 | read_flags, |
@@ -13560,14 +13506,14 @@ init(void) | |||
13560 | } | 13506 | } |
13561 | } | 13507 | } |
13562 | 13508 | ||
13563 | setvar2("PPID", utoa(getppid())); | 13509 | setvar0("PPID", utoa(getppid())); |
13564 | #if ENABLE_ASH_BASH_COMPAT | 13510 | #if ENABLE_ASH_BASH_COMPAT |
13565 | p = lookupvar("SHLVL"); | 13511 | p = lookupvar("SHLVL"); |
13566 | setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT); | 13512 | setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT); |
13567 | if (!lookupvar("HOSTNAME")) { | 13513 | if (!lookupvar("HOSTNAME")) { |
13568 | struct utsname uts; | 13514 | struct utsname uts; |
13569 | uname(&uts); | 13515 | uname(&uts); |
13570 | setvar2("HOSTNAME", uts.nodename); | 13516 | setvar0("HOSTNAME", uts.nodename); |
13571 | } | 13517 | } |
13572 | #endif | 13518 | #endif |
13573 | p = lookupvar("PWD"); | 13519 | p = lookupvar("PWD"); |
@@ -13848,7 +13794,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
13848 | hp = lookupvar("HOME"); | 13794 | hp = lookupvar("HOME"); |
13849 | if (hp) { | 13795 | if (hp) { |
13850 | hp = concat_path_file(hp, ".ash_history"); | 13796 | hp = concat_path_file(hp, ".ash_history"); |
13851 | setvar2("HISTFILE", hp); | 13797 | setvar0("HISTFILE", hp); |
13852 | free((char*)hp); | 13798 | free((char*)hp); |
13853 | hp = lookupvar("HISTFILE"); | 13799 | hp = lookupvar("HISTFILE"); |
13854 | } | 13800 | } |
diff --git a/shell/ash_test/ash-heredoc/heredoc1.right b/shell/ash_test/ash-heredoc/heredoc1.right index 895f5ee80..40aa5a5fe 100644 --- a/shell/ash_test/ash-heredoc/heredoc1.right +++ b/shell/ash_test/ash-heredoc/heredoc1.right | |||
@@ -1 +1 @@ | |||
heredoc1.tests: line 3: syntax error: unexpected "then" | ./heredoc1.tests: line 3: syntax error: unexpected "then" | ||
diff --git a/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right b/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right new file mode 100644 index 000000000..81a15855c --- /dev/null +++ b/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right | |||
@@ -0,0 +1,2 @@ | |||
1 | 12 | ||
2 | 9 | ||
diff --git a/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests b/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests new file mode 100755 index 000000000..e97a08a57 --- /dev/null +++ b/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | unset a | ||
2 | echo $((3 + ${a:=$((4 + 5))})) | ||
3 | echo $a | ||
diff --git a/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right b/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right new file mode 100644 index 000000000..4b9b4f038 --- /dev/null +++ b/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right | |||
@@ -0,0 +1 @@ | |||
~root | |||
diff --git a/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests b/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests new file mode 100755 index 000000000..d8eb8fc1b --- /dev/null +++ b/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | unset a | ||
2 | echo "${a:-~root}" | ||
diff --git a/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right b/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right new file mode 100644 index 000000000..030ebdeb6 --- /dev/null +++ b/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right | |||
@@ -0,0 +1 @@ | |||
/b/c/ | |||
diff --git a/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests b/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests new file mode 100755 index 000000000..fb9371467 --- /dev/null +++ b/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | a=/b/c/* | ||
2 | b=\\ | ||
3 | echo ${a%$b*} | ||
diff --git a/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.right b/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.right new file mode 100644 index 000000000..2357750c5 --- /dev/null +++ b/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.right | |||
@@ -0,0 +1 @@ | |||
:/root | |||
diff --git a/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.tests b/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.tests new file mode 100755 index 000000000..6605315d0 --- /dev/null +++ b/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | a=~root:~root | ||
2 | echo ${a#~root} | ||
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.right b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.right new file mode 100644 index 000000000..2da327222 --- /dev/null +++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.right | |||
@@ -0,0 +1 @@ | |||
a_\_z_c | |||
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.tests b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.tests new file mode 100755 index 000000000..e4529c631 --- /dev/null +++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | v="a\bc" | ||
2 | echo ${v/\\b/_\\_\z_} | ||
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.right b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.right new file mode 100644 index 000000000..7447c0a04 --- /dev/null +++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.right | |||
@@ -0,0 +1 @@ | |||
ax/yc | |||
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.tests b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.tests new file mode 100755 index 000000000..2db1db897 --- /dev/null +++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | v="abc" | ||
2 | echo ${v/b/x/y} | ||
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.right b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.right new file mode 100644 index 000000000..5ea5ff892 --- /dev/null +++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.right | |||
@@ -0,0 +1 @@ | |||
axcabc | |||
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.tests b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.tests new file mode 100755 index 000000000..0935e4509 --- /dev/null +++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | v="abcabc" | ||
2 | echo ${v/b/x} | ||
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.right b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.right new file mode 100644 index 000000000..46dd750c1 --- /dev/null +++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.right | |||
@@ -0,0 +1 @@ | |||
axcaxc | |||
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.tests b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.tests new file mode 100755 index 000000000..d8de84347 --- /dev/null +++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | v="abcabc" | ||
2 | echo ${v//b/x} | ||
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.right b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.right new file mode 100644 index 000000000..699b27b0c --- /dev/null +++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.right | |||
@@ -0,0 +1 @@ | |||
axc | |||
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.tests b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.tests new file mode 100755 index 000000000..552388877 --- /dev/null +++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | v="ab/c" | ||
2 | echo ${v/b\//x} | ||
diff --git a/shell/ash_test/ash-vars/var-runtime-quote-detection.right b/shell/ash_test/ash-vars/var-runtime-quote-detection.right new file mode 100644 index 000000000..b554d9e46 --- /dev/null +++ b/shell/ash_test/ash-vars/var-runtime-quote-detection.right | |||
@@ -0,0 +1 @@ | |||
<> | |||
diff --git a/shell/ash_test/ash-vars/var-runtime-quote-detection.tests b/shell/ash_test/ash-vars/var-runtime-quote-detection.tests new file mode 100755 index 000000000..e570631fd --- /dev/null +++ b/shell/ash_test/ash-vars/var-runtime-quote-detection.tests | |||
@@ -0,0 +1 @@ | |||
foo=\\ echo "<${foo#[\\]}>" | |||
diff --git a/shell/ash_test/ash-vars/var3.right b/shell/ash_test/ash-vars/var3.right new file mode 100644 index 000000000..8eb0e3337 --- /dev/null +++ b/shell/ash_test/ash-vars/var3.right | |||
@@ -0,0 +1,5 @@ | |||
1 | 1 | ||
2 | 1 | ||
3 | |||
4 | |||
5 | 0 | ||
diff --git a/shell/ash_test/ash-vars/var3.tests b/shell/ash_test/ash-vars/var3.tests new file mode 100755 index 000000000..97b102cbe --- /dev/null +++ b/shell/ash_test/ash-vars/var3.tests | |||
@@ -0,0 +1 @@ | |||
x=0; f() { local x=1; echo $x; local x; echo $x; unset x; echo $x; local x; echo $x; }; f; echo $x | |||
diff --git a/shell/hush_test/hush-bugs/var3.right b/shell/hush_test/hush-bugs/var3.right new file mode 100644 index 000000000..8eb0e3337 --- /dev/null +++ b/shell/hush_test/hush-bugs/var3.right | |||
@@ -0,0 +1,5 @@ | |||
1 | 1 | ||
2 | 1 | ||
3 | |||
4 | |||
5 | 0 | ||
diff --git a/shell/hush_test/hush-bugs/var3.tests b/shell/hush_test/hush-bugs/var3.tests new file mode 100755 index 000000000..97b102cbe --- /dev/null +++ b/shell/hush_test/hush-bugs/var3.tests | |||
@@ -0,0 +1 @@ | |||
x=0; f() { local x=1; echo $x; local x; echo $x; unset x; echo $x; local x; echo $x; }; f; echo $x | |||
diff --git a/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right b/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right new file mode 100644 index 000000000..81a15855c --- /dev/null +++ b/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right | |||
@@ -0,0 +1,2 @@ | |||
1 | 12 | ||
2 | 9 | ||
diff --git a/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests b/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests new file mode 100755 index 000000000..e97a08a57 --- /dev/null +++ b/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | unset a | ||
2 | echo $((3 + ${a:=$((4 + 5))})) | ||
3 | echo $a | ||
diff --git a/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right b/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right new file mode 100644 index 000000000..4b9b4f038 --- /dev/null +++ b/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right | |||
@@ -0,0 +1 @@ | |||
~root | |||
diff --git a/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests b/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests new file mode 100755 index 000000000..d8eb8fc1b --- /dev/null +++ b/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | unset a | ||
2 | echo "${a:-~root}" | ||
diff --git a/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right b/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right new file mode 100644 index 000000000..030ebdeb6 --- /dev/null +++ b/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right | |||
@@ -0,0 +1 @@ | |||
/b/c/ | |||
diff --git a/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests b/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests new file mode 100755 index 000000000..fb9371467 --- /dev/null +++ b/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | a=/b/c/* | ||
2 | b=\\ | ||
3 | echo ${a%$b*} | ||
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.right b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.right new file mode 100644 index 000000000..2da327222 --- /dev/null +++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.right | |||
@@ -0,0 +1 @@ | |||
a_\_z_c | |||
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.tests b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.tests new file mode 100755 index 000000000..e4529c631 --- /dev/null +++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | v="a\bc" | ||
2 | echo ${v/\\b/_\\_\z_} | ||
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.right b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.right new file mode 100644 index 000000000..7447c0a04 --- /dev/null +++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.right | |||
@@ -0,0 +1 @@ | |||
ax/yc | |||
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.tests b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.tests new file mode 100755 index 000000000..2db1db897 --- /dev/null +++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | v="abc" | ||
2 | echo ${v/b/x/y} | ||
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.right b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.right new file mode 100644 index 000000000..5ea5ff892 --- /dev/null +++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.right | |||
@@ -0,0 +1 @@ | |||
axcabc | |||
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.tests b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.tests new file mode 100755 index 000000000..0935e4509 --- /dev/null +++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | v="abcabc" | ||
2 | echo ${v/b/x} | ||
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.right b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.right new file mode 100644 index 000000000..46dd750c1 --- /dev/null +++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.right | |||
@@ -0,0 +1 @@ | |||
axcaxc | |||
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.tests b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.tests new file mode 100755 index 000000000..d8de84347 --- /dev/null +++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | v="abcabc" | ||
2 | echo ${v//b/x} | ||
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.right b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.right new file mode 100644 index 000000000..699b27b0c --- /dev/null +++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.right | |||
@@ -0,0 +1 @@ | |||
axc | |||
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.tests b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.tests new file mode 100755 index 000000000..552388877 --- /dev/null +++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | v="ab/c" | ||
2 | echo ${v/b\//x} | ||
diff --git a/shell/hush_test/hush-vars/var-runtime-quote-detection.right b/shell/hush_test/hush-vars/var-runtime-quote-detection.right new file mode 100644 index 000000000..b554d9e46 --- /dev/null +++ b/shell/hush_test/hush-vars/var-runtime-quote-detection.right | |||
@@ -0,0 +1 @@ | |||
<> | |||
diff --git a/shell/hush_test/hush-vars/var-runtime-quote-detection.tests b/shell/hush_test/hush-vars/var-runtime-quote-detection.tests new file mode 100755 index 000000000..e570631fd --- /dev/null +++ b/shell/hush_test/hush-vars/var-runtime-quote-detection.tests | |||
@@ -0,0 +1 @@ | |||
foo=\\ echo "<${foo#[\\]}>" | |||
diff --git a/testsuite/bzcat.tests b/testsuite/bzcat.tests index 1c1fd6563..9a1c28425 100755 --- a/testsuite/bzcat.tests +++ b/testsuite/bzcat.tests | |||
@@ -2,8 +2,6 @@ | |||
2 | 2 | ||
3 | FAILCOUNT=0 | 3 | FAILCOUNT=0 |
4 | 4 | ||
5 | ext=bz2 | ||
6 | |||
7 | bb="busybox " | 5 | bb="busybox " |
8 | 6 | ||
9 | unset LC_ALL | 7 | unset LC_ALL |
@@ -11,6 +9,11 @@ unset LC_MESSAGES | |||
11 | unset LANG | 9 | unset LANG |
12 | unset LANGUAGE | 10 | unset LANGUAGE |
13 | 11 | ||
12 | hello_Z() { | ||
13 | # Compressed "HELLO\n" | ||
14 | $ECHO -ne "\x1f\x9d\x90\x48\x8a\x30\x61\xf2\x44\x01" | ||
15 | } | ||
16 | |||
14 | hello_gz() { | 17 | hello_gz() { |
15 | # Gzipped "HELLO\n" | 18 | # Gzipped "HELLO\n" |
16 | #_________________________ vvv vvv vvv vvv - mtime | 19 | #_________________________ vvv vvv vvv vvv - mtime |
@@ -25,32 +28,34 @@ hello_bz2() { | |||
25 | $ECHO -ne "\x17\x72\x45\x38\x50\x90\x5b\xb8\xe8\xa3" | 28 | $ECHO -ne "\x17\x72\x45\x38\x50\x90\x5b\xb8\xe8\xa3" |
26 | } | 29 | } |
27 | 30 | ||
28 | prep() { | 31 | for ext in gz bz2 Z |
29 | rm -f t* | 32 | do |
30 | hello_$ext >t1.$ext | 33 | prep() { |
31 | hello_$ext >t2.$ext | 34 | rm -f t1.$ext t2.$ext t_actual |
32 | } | 35 | hello_$ext >t1.$ext |
36 | hello_$ext >t2.$ext | ||
37 | } | ||
33 | 38 | ||
34 | check() { | 39 | check() { |
35 | eval $2 >t_actual 2>&1 | 40 | eval $2 >t_actual 2>&1 |
36 | if $ECHO -ne "$expected" | cmp - t_actual; then | 41 | if $ECHO -ne "$expected" | cmp - t_actual; then |
37 | echo "PASS: $1" | 42 | echo "PASS: $1" |
38 | else | 43 | else |
39 | echo "FAIL: $1" | 44 | echo "FAIL: $1" |
40 | FAILCOUNT=$((FAILCOUNT + 1)) | 45 | FAILCOUNT=$((FAILCOUNT + 1)) |
41 | fi | 46 | fi |
42 | } | 47 | } |
43 | 48 | ||
44 | mkdir testdir 2>/dev/null | 49 | mkdir testdir 2>/dev/null |
45 | ( | 50 | ( |
46 | cd testdir || { echo "cannot cd testdir!"; exit 1; } | 51 | cd testdir || { echo "cannot cd testdir!"; exit 1; } |
47 | 52 | ||
48 | expected="HELLO\nok\n" | 53 | expected="HELLO\nok\n" |
49 | prep; check "bzcat: dont delete src" "${bb}bzcat t2.bz2; test -f t2.bz2 && echo ok" | 54 | prep; check "zcat: dont delete $ext src" "${bb}zcat t2.$ext; test -f t2.$ext && echo ok" |
50 | |||
51 | ) | ||
52 | rm -rf testdir | ||
53 | 55 | ||
56 | ) | ||
57 | rm -rf testdir | ||
58 | done | ||
54 | 59 | ||
55 | 60 | ||
56 | # Copyright 2011 by Denys Vlasenko | 61 | # Copyright 2011 by Denys Vlasenko |
@@ -60,6 +65,8 @@ rm -rf testdir | |||
60 | 65 | ||
61 | # testing "test name" "command" "expected result" "file input" "stdin" | 66 | # testing "test name" "command" "expected result" "file input" "stdin" |
62 | 67 | ||
68 | ## bzip algorithm | ||
69 | |||
63 | # "input" file is bzipped file with "a\n" data | 70 | # "input" file is bzipped file with "a\n" data |
64 | testing "bzcat can print many files" \ | 71 | testing "bzcat can print many files" \ |
65 | "$ECHO -ne '$hexdump' | bzcat input input; echo \$?" \ | 72 | "$ECHO -ne '$hexdump' | bzcat input input; echo \$?" \ |
@@ -79,6 +86,25 @@ testing "bzcat can handle compressed zero-length bzip2 files" \ | |||
79 | "0\n" \ | 86 | "0\n" \ |
80 | "\x42\x5a\x68\x39\x17\x72\x45\x38\x50\x90\x00\x00\x00\x00" "" | 87 | "\x42\x5a\x68\x39\x17\x72\x45\x38\x50\x90\x00\x00\x00\x00" "" |
81 | 88 | ||
89 | ## compress algorithm | ||
90 | |||
91 | # "input" file is compressed (.Z) file with "a\n" data | ||
92 | testing "zcat can print many files" \ | ||
93 | "$ECHO -ne '$hexdump' | zcat input input; echo \$?" \ | ||
94 | "\ | ||
95 | a | ||
96 | a | ||
97 | 0 | ||
98 | " "\ | ||
99 | \x1f\x9d\x90\x61\x14\x00\ | ||
100 | " "" | ||
101 | |||
102 | # "input" file is compressed (.Z) zero byte file | ||
103 | testing "zcat can handle compressed zero-length compressed (.Z) files" \ | ||
104 | "$ECHO -ne '$hexdump' | zcat input input; echo \$?" \ | ||
105 | "0\n" \ | ||
106 | "\x1f\x9d\x90\x00" "" | ||
107 | |||
82 | 108 | ||
83 | 109 | ||
84 | exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255)) | 110 | exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255)) |
diff --git a/testsuite/sed.tests b/testsuite/sed.tests index 19f2915ce..34479e55f 100755 --- a/testsuite/sed.tests +++ b/testsuite/sed.tests | |||
@@ -333,6 +333,38 @@ testing "sed s///NUM test" \ | |||
333 | "sed -e 's/a/b/2; s/a/c/g'" \ | 333 | "sed -e 's/a/b/2; s/a/c/g'" \ |
334 | "cb\n" "" "aa\n" | 334 | "cb\n" "" "aa\n" |
335 | 335 | ||
336 | testing "sed /regex/,N{...} addresses work" \ | ||
337 | "sed /^2/,2{d}" \ | ||
338 | "1\n3\n4\n5\n" \ | ||
339 | "" \ | ||
340 | "1\n2\n3\n4\n5\n" | ||
341 | |||
342 | testing "sed /regex/,+N{...} addresses work" \ | ||
343 | "sed /^2/,+2{d}" \ | ||
344 | "1\n5\n" \ | ||
345 | "" \ | ||
346 | "1\n2\n3\n4\n5\n" | ||
347 | |||
348 | testing "sed /regex/,+N{...} -i works" \ | ||
349 | "cat - >input2; sed /^4/,+2{d} -i input input2; echo \$?; cat input input2; rm input2" \ | ||
350 | "0\n""1\n2\n3\n7\n8\n""1\n2\n7\n8\n" \ | ||
351 | "1\n2\n3\n4\n5\n6\n7\n8\n" \ | ||
352 | "1\n2\n4\n5\n6\n7\n8\n" \ | ||
353 | |||
354 | # GNU sed 4.2.1 would also accept "/^4/,+{d}" with the same meaning, we don't | ||
355 | testing "sed /regex/,+0{...} -i works" \ | ||
356 | "cat - >input2; sed /^4/,+0{d} -i input input2; echo \$?; cat input input2; rm input2" \ | ||
357 | "0\n""1\n2\n3\n5\n6\n7\n8\n""1\n2\n5\n6\n7\n8\n" \ | ||
358 | "1\n2\n3\n4\n5\n6\n7\n8\n" \ | ||
359 | "1\n2\n4\n5\n6\n7\n8\n" \ | ||
360 | |||
361 | # GNU sed 4.2.1 would also accept "/^4/,+d" with the same meaning, we don't | ||
362 | testing "sed /regex/,+0<cmd> -i works" \ | ||
363 | "cat - >input2; sed /^4/,+0d -i input input2; echo \$?; cat input input2; rm input2" \ | ||
364 | "0\n""1\n2\n3\n5\n6\n7\n8\n""1\n2\n5\n6\n7\n8\n" \ | ||
365 | "1\n2\n3\n4\n5\n6\n7\n8\n" \ | ||
366 | "1\n2\n4\n5\n6\n7\n8\n" \ | ||
367 | |||
336 | # testing "description" "commands" "result" "infile" "stdin" | 368 | # testing "description" "commands" "result" "infile" "stdin" |
337 | 369 | ||
338 | exit $FAILCOUNT | 370 | exit $FAILCOUNT |
diff --git a/util-linux/mdev.c b/util-linux/mdev.c index ccc00d365..ca4b91510 100644 --- a/util-linux/mdev.c +++ b/util-linux/mdev.c | |||
@@ -283,7 +283,7 @@ struct globals { | |||
283 | unsigned rule_idx; | 283 | unsigned rule_idx; |
284 | #endif | 284 | #endif |
285 | struct rule cur_rule; | 285 | struct rule cur_rule; |
286 | char timestr[sizeof("60.123456")]; | 286 | char timestr[sizeof("HH:MM:SS.123456")]; |
287 | } FIX_ALIASING; | 287 | } FIX_ALIASING; |
288 | #define G (*(struct globals*)&bb_common_bufsiz1) | 288 | #define G (*(struct globals*)&bb_common_bufsiz1) |
289 | #define INIT_G() do { \ | 289 | #define INIT_G() do { \ |
@@ -923,7 +923,11 @@ static char *curtime(void) | |||
923 | { | 923 | { |
924 | struct timeval tv; | 924 | struct timeval tv; |
925 | gettimeofday(&tv, NULL); | 925 | gettimeofday(&tv, NULL); |
926 | sprintf(G.timestr, "%u.%06u", (unsigned)tv.tv_sec % 60, (unsigned)tv.tv_usec); | 926 | sprintf( |
927 | strftime_HHMMSS(G.timestr, sizeof(G.timestr), &tv.tv_sec), | ||
928 | ".%06u", | ||
929 | (unsigned)tv.tv_usec | ||
930 | ); | ||
927 | return G.timestr; | 931 | return G.timestr; |
928 | } | 932 | } |
929 | 933 | ||
@@ -943,7 +947,7 @@ static void open_mdev_log(const char *seq, unsigned my_pid) | |||
943 | * Active mdev pokes us with SIGCHLD to check the new file. | 947 | * Active mdev pokes us with SIGCHLD to check the new file. |
944 | */ | 948 | */ |
945 | static int | 949 | static int |
946 | wait_for_seqfile(const char *seq) | 950 | wait_for_seqfile(unsigned expected_seq) |
947 | { | 951 | { |
948 | /* We time out after 2 sec */ | 952 | /* We time out after 2 sec */ |
949 | static const struct timespec ts = { 0, 32*1000*1000 }; | 953 | static const struct timespec ts = { 0, 32*1000*1000 }; |
@@ -958,12 +962,14 @@ wait_for_seqfile(const char *seq) | |||
958 | 962 | ||
959 | for (;;) { | 963 | for (;;) { |
960 | int seqlen; | 964 | int seqlen; |
961 | char seqbuf[sizeof(int)*3 + 2]; | 965 | char seqbuf[sizeof(long)*3 + 2]; |
966 | unsigned seqbufnum; | ||
962 | 967 | ||
963 | if (seq_fd < 0) { | 968 | if (seq_fd < 0) { |
964 | seq_fd = open("mdev.seq", O_RDWR); | 969 | seq_fd = open("mdev.seq", O_RDWR); |
965 | if (seq_fd < 0) | 970 | if (seq_fd < 0) |
966 | break; | 971 | break; |
972 | close_on_exec_on(seq_fd); | ||
967 | } | 973 | } |
968 | seqlen = pread(seq_fd, seqbuf, sizeof(seqbuf) - 1, 0); | 974 | seqlen = pread(seq_fd, seqbuf, sizeof(seqbuf) - 1, 0); |
969 | if (seqlen < 0) { | 975 | if (seqlen < 0) { |
@@ -974,17 +980,25 @@ wait_for_seqfile(const char *seq) | |||
974 | seqbuf[seqlen] = '\0'; | 980 | seqbuf[seqlen] = '\0'; |
975 | if (seqbuf[0] == '\n' || seqbuf[0] == '\0') { | 981 | if (seqbuf[0] == '\n' || seqbuf[0] == '\0') { |
976 | /* seed file: write out seq ASAP */ | 982 | /* seed file: write out seq ASAP */ |
977 | xwrite_str(seq_fd, seq); | 983 | xwrite_str(seq_fd, utoa(expected_seq)); |
978 | xlseek(seq_fd, 0, SEEK_SET); | 984 | xlseek(seq_fd, 0, SEEK_SET); |
979 | dbg2("first seq written"); | 985 | dbg2("first seq written"); |
980 | break; | 986 | break; |
981 | } | 987 | } |
982 | if (strcmp(seq, seqbuf) == 0) { | 988 | seqbufnum = atoll(seqbuf); |
989 | if (seqbufnum == expected_seq) { | ||
983 | /* correct idx */ | 990 | /* correct idx */ |
984 | break; | 991 | break; |
985 | } | 992 | } |
993 | if (seqbufnum > expected_seq) { | ||
994 | /* a later mdev runs already (this was seen by users to happen) */ | ||
995 | /* do not overwrite seqfile on exit */ | ||
996 | close(seq_fd); | ||
997 | seq_fd = -1; | ||
998 | break; | ||
999 | } | ||
986 | if (do_once) { | 1000 | if (do_once) { |
987 | dbg2("%s waiting for '%s'", curtime(), seqbuf); | 1001 | dbg2("%s mdev.seq='%s', need '%u'", curtime(), seqbuf, expected_seq); |
988 | do_once = 0; | 1002 | do_once = 0; |
989 | } | 1003 | } |
990 | if (sigtimedwait(&set_CHLD, NULL, &ts) >= 0) { | 1004 | if (sigtimedwait(&set_CHLD, NULL, &ts) >= 0) { |
@@ -992,7 +1006,7 @@ wait_for_seqfile(const char *seq) | |||
992 | continue; /* don't decrement timeout! */ | 1006 | continue; /* don't decrement timeout! */ |
993 | } | 1007 | } |
994 | if (--timeout == 0) { | 1008 | if (--timeout == 0) { |
995 | dbg1("%s waiting for '%s'", "timed out", seqbuf); | 1009 | dbg1("%s mdev.seq='%s'", "timed out", seqbuf); |
996 | break; | 1010 | break; |
997 | } | 1011 | } |
998 | } | 1012 | } |
@@ -1075,6 +1089,7 @@ int mdev_main(int argc UNUSED_PARAM, char **argv) | |||
1075 | char *env_devname; | 1089 | char *env_devname; |
1076 | char *env_devpath; | 1090 | char *env_devpath; |
1077 | unsigned my_pid; | 1091 | unsigned my_pid; |
1092 | unsigned seqnum = seqnum; /* for compiler */ | ||
1078 | int seq_fd; | 1093 | int seq_fd; |
1079 | smalluint op; | 1094 | smalluint op; |
1080 | 1095 | ||
@@ -1096,7 +1111,11 @@ int mdev_main(int argc UNUSED_PARAM, char **argv) | |||
1096 | my_pid = getpid(); | 1111 | my_pid = getpid(); |
1097 | open_mdev_log(seq, my_pid); | 1112 | open_mdev_log(seq, my_pid); |
1098 | 1113 | ||
1099 | seq_fd = seq ? wait_for_seqfile(seq) : -1; | 1114 | seq_fd = -1; |
1115 | if (seq) { | ||
1116 | seqnum = atoll(seq); | ||
1117 | seq_fd = wait_for_seqfile(seqnum); | ||
1118 | } | ||
1100 | 1119 | ||
1101 | dbg1("%s " | 1120 | dbg1("%s " |
1102 | "ACTION:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s" | 1121 | "ACTION:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s" |
@@ -1124,7 +1143,7 @@ int mdev_main(int argc UNUSED_PARAM, char **argv) | |||
1124 | 1143 | ||
1125 | dbg1("%s exiting", curtime()); | 1144 | dbg1("%s exiting", curtime()); |
1126 | if (seq_fd >= 0) { | 1145 | if (seq_fd >= 0) { |
1127 | xwrite_str(seq_fd, utoa(xatou(seq) + 1)); | 1146 | xwrite_str(seq_fd, utoa(seqnum + 1)); |
1128 | signal_mdevs(my_pid); | 1147 | signal_mdevs(my_pid); |
1129 | } | 1148 | } |
1130 | } | 1149 | } |
diff --git a/util-linux/uevent.c b/util-linux/uevent.c new file mode 100644 index 000000000..fb98b4845 --- /dev/null +++ b/util-linux/uevent.c | |||
@@ -0,0 +1,127 @@ | |||
1 | /* | ||
2 | * Copyright 2015 Denys Vlasenko | ||
3 | * | ||
4 | * Licensed under GPLv2, see file LICENSE in this source tree. | ||
5 | */ | ||
6 | |||
7 | //config:config UEVENT | ||
8 | //config: bool "uevent" | ||
9 | //config: default y | ||
10 | //config: select PLATFORM_LINUX | ||
11 | //config: help | ||
12 | //config: uevent is a netlink listener for kernel uevent notifications | ||
13 | //config: sent via netlink. It is usually used for dynamic device creation. | ||
14 | |||
15 | //applet:IF_UEVENT(APPLET(uevent, BB_DIR_SBIN, BB_SUID_DROP)) | ||
16 | |||
17 | //kbuild:lib-$(CONFIG_UEVENT) += uevent.o | ||
18 | |||
19 | //usage:#define uevent_trivial_usage | ||
20 | //usage: "[PROG [ARGS]]" | ||
21 | //usage:#define uevent_full_usage "\n\n" | ||
22 | //usage: "uevent runs PROG for every netlink notification." | ||
23 | //usage: "\n""PROG's environment contains data passed from the kernel." | ||
24 | //usage: "\n""Typical usage (daemon for dynamic device node creation):" | ||
25 | //usage: "\n"" # uevent mdev & mdev -s" | ||
26 | |||
27 | #include "libbb.h" | ||
28 | #include <linux/netlink.h> | ||
29 | |||
30 | #define BUFFER_SIZE 16*1024 | ||
31 | |||
32 | #define env ((char **)&bb_common_bufsiz1) | ||
33 | enum { | ||
34 | MAX_ENV = COMMON_BUFSIZE / sizeof(env[0]) - 1, | ||
35 | }; | ||
36 | |||
37 | #ifndef SO_RCVBUFFORCE | ||
38 | #define SO_RCVBUFFORCE 33 | ||
39 | #endif | ||
40 | static const int RCVBUF = 2 * 1024 * 1024; | ||
41 | |||
42 | int uevent_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
43 | int uevent_main(int argc UNUSED_PARAM, char **argv) | ||
44 | { | ||
45 | struct sockaddr_nl sa; | ||
46 | int fd; | ||
47 | |||
48 | argv++; | ||
49 | |||
50 | // Subscribe for UEVENT kernel messages | ||
51 | sa.nl_family = AF_NETLINK; | ||
52 | sa.nl_pad = 0; | ||
53 | sa.nl_pid = getpid(); | ||
54 | sa.nl_groups = 1 << 0; | ||
55 | fd = xsocket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); | ||
56 | xbind(fd, (struct sockaddr *) &sa, sizeof(sa)); | ||
57 | close_on_exec_on(fd); | ||
58 | |||
59 | // Without a sufficiently big RCVBUF, a ton of simultaneous events | ||
60 | // can trigger ENOBUFS on read, which is unrecoverable. | ||
61 | // Reproducer: | ||
62 | // uevent mdev & | ||
63 | // find /sys -name uevent -exec sh -c 'echo add >"{}"' ';' | ||
64 | // | ||
65 | // SO_RCVBUFFORCE (root only) can go above net.core.rmem_max sysctl | ||
66 | setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &RCVBUF, sizeof(RCVBUF)); | ||
67 | setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &RCVBUF, sizeof(RCVBUF)); | ||
68 | if (0) { | ||
69 | int z; | ||
70 | socklen_t zl = sizeof(z); | ||
71 | getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &z, &zl); | ||
72 | bb_error_msg("SO_RCVBUF:%d", z); | ||
73 | } | ||
74 | |||
75 | for (;;) { | ||
76 | char *netbuf; | ||
77 | char *s, *end; | ||
78 | ssize_t len; | ||
79 | int idx; | ||
80 | |||
81 | // In many cases, a system sits for *days* waiting | ||
82 | // for a new uevent notification to come in. | ||
83 | // We use a fresh mmap so that buffer is not allocated | ||
84 | // until kernel actually starts filling it. | ||
85 | netbuf = mmap(NULL, BUFFER_SIZE, | ||
86 | PROT_READ | PROT_WRITE, | ||
87 | MAP_PRIVATE | MAP_ANON, | ||
88 | /* ignored: */ -1, 0); | ||
89 | if (netbuf == MAP_FAILED) | ||
90 | bb_perror_msg_and_die("mmap"); | ||
91 | |||
92 | // Here we block, possibly for a very long time | ||
93 | len = safe_read(fd, netbuf, BUFFER_SIZE - 1); | ||
94 | if (len < 0) | ||
95 | bb_perror_msg_and_die("read"); | ||
96 | end = netbuf + len; | ||
97 | *end = '\0'; | ||
98 | |||
99 | // Each netlink message starts with "ACTION@/path" | ||
100 | // (which we currently ignore), | ||
101 | // followed by environment variables. | ||
102 | if (!argv[0]) | ||
103 | putchar('\n'); | ||
104 | idx = 0; | ||
105 | s = netbuf; | ||
106 | while (s < end) { | ||
107 | if (!argv[0]) | ||
108 | puts(s); | ||
109 | if (strchr(s, '=') && idx < MAX_ENV) | ||
110 | env[idx++] = s; | ||
111 | s += strlen(s) + 1; | ||
112 | } | ||
113 | env[idx] = NULL; | ||
114 | |||
115 | idx = 0; | ||
116 | while (env[idx]) | ||
117 | putenv(env[idx++]); | ||
118 | if (argv[0]) | ||
119 | spawn_and_wait(argv); | ||
120 | idx = 0; | ||
121 | while (env[idx]) | ||
122 | bb_unsetenv(env[idx++]); | ||
123 | munmap(netbuf, BUFFER_SIZE); | ||
124 | } | ||
125 | |||
126 | return 0; // not reached | ||
127 | } | ||
diff --git a/util-linux/volume_id/get_devname.c b/util-linux/volume_id/get_devname.c index 53bdbdf09..6b97df113 100644 --- a/util-linux/volume_id/get_devname.c +++ b/util-linux/volume_id/get_devname.c | |||
@@ -304,7 +304,7 @@ int resolve_mount_spec(char **fsname) | |||
304 | 304 | ||
305 | if (is_prefixed_with(*fsname, "UUID=")) | 305 | if (is_prefixed_with(*fsname, "UUID=")) |
306 | tmp = get_devname_from_uuid(*fsname + 5); | 306 | tmp = get_devname_from_uuid(*fsname + 5); |
307 | else if (is_prefixed_with(*fsname, "LABEL=") == 0) | 307 | else if (is_prefixed_with(*fsname, "LABEL=")) |
308 | tmp = get_devname_from_label(*fsname + 6); | 308 | tmp = get_devname_from_label(*fsname + 6); |
309 | 309 | ||
310 | if (tmp == *fsname) | 310 | if (tmp == *fsname) |