aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2015-05-18 09:36:27 +0100
committerRon Yorston <rmy@pobox.com>2015-05-18 09:36:27 +0100
commit60063627a6d540871061854a362047e6517f821c (patch)
tree0de228630450c64e085f2e3f5141b5ba17eccab3
parentec39cb770ddd5c0e085d5c4ee10be65bab5e7a44 (diff)
parent9a595bb36ded308e6d4336aef2c1cd3ac738a398 (diff)
downloadbusybox-w32-60063627a6d540871061854a362047e6517f821c.tar.gz
busybox-w32-60063627a6d540871061854a362047e6517f821c.tar.bz2
busybox-w32-60063627a6d540871061854a362047e6517f821c.zip
Merge branch 'busybox' into mergeFRP
-rw-r--r--Makefile.flags2
-rw-r--r--README2
-rw-r--r--archival/gzip.c72
-rw-r--r--archival/libarchive/get_header_tar.c28
-rw-r--r--archival/libarchive/open_transformer.c7
-rw-r--r--coreutils/false.c8
-rw-r--r--coreutils/test.c11
-rw-r--r--coreutils/true.c8
-rw-r--r--coreutils/who.c8
-rw-r--r--e2fsprogs/old_e2fsprogs/e2fsck.c2
-rw-r--r--editors/sed.c41
-rw-r--r--editors/vi.c10
-rwxr-xr-xexamples/android-build2
-rwxr-xr-xexamples/var_service/ntpd/ntp.script21
-rw-r--r--findutils/find.c6
-rw-r--r--include/libbb.h27
-rw-r--r--include/platform.h20
-rw-r--r--init/halt.c4
-rw-r--r--libbb/appletlib.c24
-rw-r--r--libbb/change_identity.c23
-rw-r--r--libbb/missing_syscalls.c5
-rw-r--r--libbb/platform.c19
-rw-r--r--libbb/read_printf.c8
-rw-r--r--libbb/utmp.c44
-rw-r--r--miscutils/i2c_tools.c13
-rw-r--r--miscutils/last.c8
-rw-r--r--miscutils/last_fancy.c16
-rw-r--r--miscutils/runlevel.c12
-rw-r--r--miscutils/wall.c8
-rw-r--r--networking/ftpd.c41
-rw-r--r--procps/uptime.c6
-rw-r--r--shell/ash.c388
-rw-r--r--shell/ash_test/ash-heredoc/heredoc1.right2
-rw-r--r--shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right2
-rwxr-xr-xshell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests3
-rw-r--r--shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests2
-rw-r--r--shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests3
-rw-r--r--shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.tests2
-rw-r--r--shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.tests2
-rw-r--r--shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.tests2
-rw-r--r--shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.tests2
-rw-r--r--shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.tests2
-rw-r--r--shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.tests2
-rw-r--r--shell/ash_test/ash-vars/var-runtime-quote-detection.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-runtime-quote-detection.tests1
-rw-r--r--shell/ash_test/ash-vars/var3.right5
-rwxr-xr-xshell/ash_test/ash-vars/var3.tests1
-rw-r--r--shell/hush_test/hush-bugs/var3.right5
-rwxr-xr-xshell/hush_test/hush-bugs/var3.tests1
-rw-r--r--shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right2
-rwxr-xr-xshell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests3
-rw-r--r--shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests2
-rw-r--r--shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests3
-rw-r--r--shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.tests2
-rw-r--r--shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.tests2
-rw-r--r--shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.tests2
-rw-r--r--shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.tests2
-rw-r--r--shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.tests2
-rw-r--r--shell/hush_test/hush-vars/var-runtime-quote-detection.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-runtime-quote-detection.tests1
-rwxr-xr-xtestsuite/bzcat.tests74
-rwxr-xr-xtestsuite/sed.tests32
-rw-r--r--util-linux/mdev.c39
-rw-r--r--util-linux/uevent.c127
-rw-r--r--util-linux/volume_id/get_devname.c2
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
170endif 170endif
171 171
172ifneq ($(CONFIG_EXTRA_LDFLAGS),) 172ifneq ($(CONFIG_EXTRA_LDFLAGS),)
173EXTRA_LDFLAGS += $(strip $(subst ",,$(CONFIG_EXTRA_LDFLAGS))) 173LDFLAGS += $(strip $(subst ",,$(CONFIG_EXTRA_LDFLAGS)))
174#")) 174#"))
175endif 175endif
176 176
diff --git a/README b/README
index 82f72e5a8..9936e6cc3 100644
--- a/README
+++ b/README
@@ -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
289struct globals { 305struct 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)
73int who_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 73int who_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
74int who_main(int argc UNUSED_PARAM, char **argv) 74int 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"
30fi 30fi
31 31
32# It's possible with newer version
33# you need to use CFLAGS_busybox instead of EXTRA_LDFLAGS below:
32make EXTRA_LDFLAGS="$LDFLAGS" LDLIBS="$LDLIBS" "$@" 34make 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
11dt=`date '+%Y-%m-%d %H:%M:%S'` 11dt=`date '+%Y-%m-%d %H:%M:%S'`
12 12
13echo "`tail -n 199 -- "$0.log" 2>/dev/null`" >"$0.log.$$"
14
15if 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
30fi
31
13if test x"$stratum" != x"" \ 32if 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
28fi 46fi
29 47
30echo "`tail -n 199 -- "$0.log" 2>/dev/null`" >"$0.log.$$"
31echo "$dt: $1"\ 48echo "$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();)
1469out:
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
728extern ssize_t safe_read(int fd, void *buf, size_t count) FAST_FUNC; 751extern ssize_t safe_read(int fd, void *buf, size_t count) FAST_FUNC;
729extern ssize_t nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR) FAST_FUNC; 752extern 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
732extern ssize_t full_read(int fd, void *buf, size_t count) FAST_FUNC; 755extern 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;
542extern char *mkdtemp(char *template) FAST_FUNC; 551extern char *mkdtemp(char *template) FAST_FUNC;
543#endif 552#endif
544 553
554#ifndef HAVE_TTYNAME_R
555#define ttyname_r bb_ttyname_r
556extern 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
75static void write_wtmp(void) 75static 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. */
34void FAST_FUNC change_identity(const struct passwd *pw) 34void 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
43int 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
199int 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 */
48ssize_t FAST_FUNC nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR) 48ssize_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
17void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname) 17void 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 */
65void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname) 65void 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
46int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 46int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
47int last_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 47int 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
25enum { 29enum {
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
42static void show_entry(struct utmp *ut, int state, time_t dur_secs) 46static 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
107static int get_ut_type(struct utmp *ut) 111static 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
145static int is_runlevel_shutdown(struct utmp *ut) 149static 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)
154int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 158int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
155int last_main(int argc UNUSED_PARAM, char **argv) 159int 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 @@
29int runlevel_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 29int runlevel_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
30int runlevel_main(int argc UNUSED_PARAM, char **argv) 30int 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 @@
32int wall_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 32int wall_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
33int wall_main(int argc UNUSED_PARAM, char **argv) 33int 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)
1116int ftpd_main(int argc UNUSED_PARAM, char **argv) 1116int 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
698static const char dolatstr[] ALIGN1 = { 695static 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
2317static void FAST_FUNC 2312static void FAST_FUNC
2318setvar2(const char *name, const char *val) 2313setvar0(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)
5919static char * 5912static char *
5920rmescapes(char *str, int flag) 5913rmescapes(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 */
5999static char * 5998static char *
6000preglob(const char *pattern, int quoted, int flag) 5999preglob(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)
6012static void 6007static void
6013memtodest(const char *p, size_t len, int syntax, int quotes) 6008memtodest(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
6034static void 6034static size_t
6035strtodest(const char *p, int syntax, int quotes) 6035strtodest(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 */
6225static void 6227static void
6226expbackq(union node *cmd, int quoted, int quotes) 6228expbackq(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 */
6288static void 6290static void
6289expari(int quotes) 6291expari(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
6619static char *
6620parse_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
6662static const char * 6616static const char *
6663subevalvar(char *p, char *varname, int strloc, int subtype, 6617subevalvar(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
7646expandhere(union node *arg, int fd) 7592expandhere(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)
7656static int 7602static int
7657patmatch(char *pattern, const char *string) 7603patmatch(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 */
9367static void 9314static void
9368mklocal(char *name) 9315mklocal(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 @@
112
29
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 @@
1unset a
2echo $((3 + ${a:=$((4 + 5))}))
3echo $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 @@
1unset a
2echo "${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 @@
1a=/b/c/*
2b=\\
3echo ${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 @@
1a=~root:~root
2echo ${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 @@
1v="a\bc"
2echo ${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 @@
1v="abc"
2echo ${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 @@
1v="abcabc"
2echo ${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 @@
1v="abcabc"
2echo ${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 @@
1v="ab/c"
2echo ${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 @@
11
21
3
4
50
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 @@
11
21
3
4
50
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 @@
112
29
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 @@
1unset a
2echo $((3 + ${a:=$((4 + 5))}))
3echo $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 @@
1unset a
2echo "${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 @@
1a=/b/c/*
2b=\\
3echo ${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 @@
1v="a\bc"
2echo ${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 @@
1v="abc"
2echo ${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 @@
1v="abcabc"
2echo ${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 @@
1v="abcabc"
2echo ${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 @@
1v="ab/c"
2echo ${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
3FAILCOUNT=0 3FAILCOUNT=0
4 4
5ext=bz2
6
7bb="busybox " 5bb="busybox "
8 6
9unset LC_ALL 7unset LC_ALL
@@ -11,6 +9,11 @@ unset LC_MESSAGES
11unset LANG 9unset LANG
12unset LANGUAGE 10unset LANGUAGE
13 11
12hello_Z() {
13 # Compressed "HELLO\n"
14 $ECHO -ne "\x1f\x9d\x90\x48\x8a\x30\x61\xf2\x44\x01"
15}
16
14hello_gz() { 17hello_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
28prep() { 31for ext in gz bz2 Z
29 rm -f t* 32do
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
34check() { 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
44mkdir testdir 2>/dev/null 49 mkdir testdir 2>/dev/null
45( 50 (
46cd testdir || { echo "cannot cd testdir!"; exit 1; } 51 cd testdir || { echo "cannot cd testdir!"; exit 1; }
47 52
48expected="HELLO\nok\n" 53 expected="HELLO\nok\n"
49prep; 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)
52rm -rf testdir
53 55
56 )
57 rm -rf testdir
58done
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
64testing "bzcat can print many files" \ 71testing "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
92testing "zcat can print many files" \
93"$ECHO -ne '$hexdump' | zcat input input; echo \$?" \
94"\
95a
96a
970
98" "\
99\x1f\x9d\x90\x61\x14\x00\
100" ""
101
102# "input" file is compressed (.Z) zero byte file
103testing "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
84exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255)) 110exit $((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
336testing "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
342testing "sed /regex/,+N{...} addresses work" \
343 "sed /^2/,+2{d}" \
344 "1\n5\n" \
345 "" \
346 "1\n2\n3\n4\n5\n"
347
348testing "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
355testing "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
362testing "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
338exit $FAILCOUNT 370exit $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 */
945static int 949static int
946wait_for_seqfile(const char *seq) 950wait_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)
33enum {
34 MAX_ENV = COMMON_BUFSIZE / sizeof(env[0]) - 1,
35};
36
37#ifndef SO_RCVBUFFORCE
38#define SO_RCVBUFFORCE 33
39#endif
40static const int RCVBUF = 2 * 1024 * 1024;
41
42int uevent_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
43int 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)