aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2013-03-19 11:18:39 +0000
committerRon Yorston <rmy@pobox.com>2013-03-19 11:18:39 +0000
commit63d2c5fead323df5f4250ed544d0bc03527c8936 (patch)
tree660979b139a4bc4b143c08843cb7efbc69bdcb4d
parent27fc2d535588728ac3ca69337271471fb6fe3ee9 (diff)
parenta42f530e034b673726a91ea5d8202254e677f066 (diff)
downloadbusybox-w32-63d2c5fead323df5f4250ed544d0bc03527c8936.tar.gz
busybox-w32-63d2c5fead323df5f4250ed544d0bc03527c8936.tar.bz2
busybox-w32-63d2c5fead323df5f4250ed544d0bc03527c8936.zip
Merge branch 'busybox' into merge
-rw-r--r--archival/bbunzip.c77
-rw-r--r--archival/libarchive/data_extract_all.c15
-rw-r--r--archival/libarchive/decompress_unlzma.c29
-rw-r--r--archival/libarchive/decompress_unxz.c47
-rw-r--r--archival/libarchive/open_transformer.c11
-rw-r--r--archival/libarchive/unxz/README6
-rw-r--r--archival/libarchive/unxz/xz.h17
-rw-r--r--archival/libarchive/unxz/xz_dec_bcj.c34
-rw-r--r--archival/libarchive/unxz/xz_dec_lzma2.c7
-rw-r--r--archival/libarchive/unxz/xz_stream.h11
-rw-r--r--archival/lzop.c32
-rw-r--r--archival/rpm.c305
-rw-r--r--coreutils/Kbuild.src2
-rw-r--r--coreutils/head.c159
-rw-r--r--coreutils/head_tail.c14
-rw-r--r--coreutils/head_tail.h6
-rw-r--r--coreutils/hostid.c3
-rw-r--r--coreutils/id.c4
-rw-r--r--coreutils/readlink.c5
-rw-r--r--coreutils/tail.c18
-rw-r--r--debianutils/run_parts.c56
-rw-r--r--docs/mdev.txt4
-rw-r--r--editors/awk.c3
-rw-r--r--editors/vi.c16
-rw-r--r--examples/mdev_fat.conf7
-rwxr-xr-xexamples/udhcp/simple.script15
-rw-r--r--include/bb_archive.h3
-rw-r--r--include/libbb.h1
-rw-r--r--include/platform.h22
-rw-r--r--libbb/appletlib.c2
-rw-r--r--libbb/endofname.c26
-rw-r--r--libbb/lineedit.c1
-rw-r--r--libbb/platform.c10
-rw-r--r--libbb/xreadlink.c3
-rw-r--r--mailutils/sendmail.c103
-rw-r--r--miscutils/flash_eraseall.c15
-rw-r--r--miscutils/nandwrite.c8
-rw-r--r--networking/ether-wake.c5
-rw-r--r--networking/ifplugd.c3
-rw-r--r--networking/ifupdown.c19
-rw-r--r--networking/nameif.c15
-rw-r--r--networking/nc.c20
-rw-r--r--networking/nc_bloaty.c58
-rw-r--r--networking/ping.c61
-rw-r--r--networking/traceroute.c15
-rw-r--r--networking/udhcp/dhcpd.c19
-rw-r--r--procps/pgrep.c2
-rw-r--r--shell/ash.c61
-rw-r--r--shell/hush.c9
-rw-r--r--shell/hush_test/hush-misc/source2.right4
-rwxr-xr-xshell/hush_test/hush-misc/source2.tests8
-rw-r--r--shell/math.c12
-rw-r--r--shell/math.h5
-rw-r--r--sysklogd/syslogd.c2
-rwxr-xr-xtestsuite/awk.tests19
-rw-r--r--util-linux/fdisk_sun.c10
-rw-r--r--util-linux/losetup.c116
-rw-r--r--util-linux/mdev.c341
-rw-r--r--util-linux/mkfs_minix.c77
59 files changed, 1260 insertions, 718 deletions
diff --git a/archival/bbunzip.c b/archival/bbunzip.c
index 66a046052..f77ac8383 100644
--- a/archival/bbunzip.c
+++ b/archival/bbunzip.c
@@ -7,13 +7,16 @@
7#include "libbb.h" 7#include "libbb.h"
8#include "bb_archive.h" 8#include "bb_archive.h"
9 9
10/* Note: must be kept in sync with archival/lzop.c */
10enum { 11enum {
11 OPT_STDOUT = 1 << 0, 12 OPT_STDOUT = 1 << 0,
12 OPT_FORCE = 1 << 1, 13 OPT_FORCE = 1 << 1,
13 /* only some decompressors: */ 14 /* only some decompressors: */
14 OPT_VERBOSE = 1 << 2, 15 OPT_VERBOSE = 1 << 2,
15 OPT_DECOMPRESS = 1 << 3, 16 OPT_QUIET = 1 << 3,
16 OPT_TEST = 1 << 4, 17 OPT_DECOMPRESS = 1 << 4,
18 OPT_TEST = 1 << 5,
19 SEAMLESS_MAGIC = (1 << 31) * SEAMLESS_COMPRESSION,
17}; 20};
18 21
19static 22static
@@ -39,7 +42,7 @@ int FAST_FUNC bbunpack(char **argv,
39) 42)
40{ 43{
41 struct stat stat_buf; 44 struct stat stat_buf;
42 IF_DESKTOP(long long) int status; 45 IF_DESKTOP(long long) int status = 0;
43 char *filename, *new_name; 46 char *filename, *new_name;
44 smallint exitcode = 0; 47 smallint exitcode = 0;
45 transformer_aux_data_t aux; 48 transformer_aux_data_t aux;
@@ -54,13 +57,27 @@ int FAST_FUNC bbunpack(char **argv,
54 57
55 /* Open src */ 58 /* Open src */
56 if (filename) { 59 if (filename) {
57 if (stat(filename, &stat_buf) != 0) { 60 if (!(option_mask32 & SEAMLESS_MAGIC)) {
58 bb_simple_perror_msg(filename); 61 if (stat(filename, &stat_buf) != 0) {
62 err_name:
63 bb_simple_perror_msg(filename);
59 err: 64 err:
60 exitcode = 1; 65 exitcode = 1;
61 goto free_name; 66 goto free_name;
67 }
68 if (open_to_or_warn(STDIN_FILENO, filename, O_RDONLY, 0))
69 goto err;
70 } else {
71 /* "clever zcat" with FILE */
72 int fd = open_zipped(filename);
73 if (fd < 0)
74 goto err_name;
75 xmove_fd(fd, STDIN_FILENO);
62 } 76 }
63 if (open_to_or_warn(STDIN_FILENO, filename, O_RDONLY, 0)) 77 } else
78 if (option_mask32 & SEAMLESS_MAGIC) {
79 /* "clever zcat" on stdin */
80 if (setup_unzip_on_fd(STDIN_FILENO, /*fail_if_not_detected*/ 0))
64 goto err; 81 goto err;
65 } 82 }
66 83
@@ -68,7 +85,7 @@ int FAST_FUNC bbunpack(char **argv,
68 if (option_mask32 & (OPT_STDOUT|OPT_TEST)) { 85 if (option_mask32 & (OPT_STDOUT|OPT_TEST)) {
69 if (option_mask32 & OPT_TEST) 86 if (option_mask32 & OPT_TEST)
70 if (open_to_or_warn(STDOUT_FILENO, bb_dev_null, O_WRONLY, 0)) 87 if (open_to_or_warn(STDOUT_FILENO, bb_dev_null, O_WRONLY, 0))
71 goto err; 88 xfunc_die();
72 filename = NULL; 89 filename = NULL;
73 } 90 }
74 91
@@ -93,16 +110,22 @@ int FAST_FUNC bbunpack(char **argv,
93 } 110 }
94 111
95 /* Check that the input is sane */ 112 /* Check that the input is sane */
96 if (isatty(STDIN_FILENO) && (option_mask32 & OPT_FORCE) == 0) { 113 if (!(option_mask32 & OPT_FORCE) && isatty(STDIN_FILENO)) {
97 bb_error_msg_and_die("compressed data not read from terminal, " 114 bb_error_msg_and_die("compressed data not read from terminal, "
98 "use -f to force it"); 115 "use -f to force it");
99 } 116 }
100 117
101 init_transformer_aux_data(&aux); 118 if (!(option_mask32 & SEAMLESS_MAGIC)) {
102 aux.check_signature = 1; 119 init_transformer_aux_data(&aux);
103 status = unpacker(&aux); 120 aux.check_signature = 1;
104 if (status < 0) 121 status = unpacker(&aux);
105 exitcode = 1; 122 if (status < 0)
123 exitcode = 1;
124 } else {
125 if (bb_copyfd_eof(STDIN_FILENO, STDOUT_FILENO) < 0)
126 /* Disk full, tty closed, etc. No point in continuing */
127 xfunc_die();
128 }
106 129
107 if (!(option_mask32 & OPT_STDOUT)) 130 if (!(option_mask32 & OPT_STDOUT))
108 xclose(STDOUT_FILENO); /* with error check! */ 131 xclose(STDOUT_FILENO); /* with error check! */
@@ -243,7 +266,7 @@ int uncompress_main(int argc UNUSED_PARAM, char **argv)
243//usage: "-rw-rw-r-- 1 andersen andersen 1761280 Apr 14 17:47 /tmp/BusyBox-0.43.tar\n" 266//usage: "-rw-rw-r-- 1 andersen andersen 1761280 Apr 14 17:47 /tmp/BusyBox-0.43.tar\n"
244//usage: 267//usage:
245//usage:#define zcat_trivial_usage 268//usage:#define zcat_trivial_usage
246//usage: "FILE" 269//usage: "[FILE]..."
247//usage:#define zcat_full_usage "\n\n" 270//usage:#define zcat_full_usage "\n\n"
248//usage: "Decompress to stdout" 271//usage: "Decompress to stdout"
249 272
@@ -294,11 +317,15 @@ IF_DESKTOP(long long) int FAST_FUNC unpack_gunzip(transformer_aux_data_t *aux)
294int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 317int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
295int gunzip_main(int argc UNUSED_PARAM, char **argv) 318int gunzip_main(int argc UNUSED_PARAM, char **argv)
296{ 319{
297 getopt32(argv, "cfvdtn"); 320 getopt32(argv, "cfvqdtn");
298 argv += optind; 321 argv += optind;
299 /* if called as zcat */ 322
323 /* If called as zcat...
324 * Normally, "zcat" is just "gunzip -c".
325 * But if seamless magic is enabled, then we are much more clever.
326 */
300 if (applet_name[1] == 'c') 327 if (applet_name[1] == 'c')
301 option_mask32 |= OPT_STDOUT; 328 option_mask32 |= OPT_STDOUT | SEAMLESS_MAGIC;
302 329
303 return bbunpack(argv, unpack_gunzip, make_new_name_gunzip, /*unused:*/ NULL); 330 return bbunpack(argv, unpack_gunzip, make_new_name_gunzip, /*unused:*/ NULL);
304} 331}
@@ -318,7 +345,7 @@ int gunzip_main(int argc UNUSED_PARAM, char **argv)
318//usage: "\n -c Write to stdout" 345//usage: "\n -c Write to stdout"
319//usage: "\n -f Force" 346//usage: "\n -f Force"
320//usage:#define bzcat_trivial_usage 347//usage:#define bzcat_trivial_usage
321//usage: "FILE" 348//usage: "[FILE]..."
322//usage:#define bzcat_full_usage "\n\n" 349//usage:#define bzcat_full_usage "\n\n"
323//usage: "Decompress to stdout" 350//usage: "Decompress to stdout"
324//applet:IF_BUNZIP2(APPLET(bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP)) 351//applet:IF_BUNZIP2(APPLET(bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP))
@@ -332,7 +359,7 @@ IF_DESKTOP(long long) int FAST_FUNC unpack_bunzip2(transformer_aux_data_t *aux)
332int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 359int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
333int bunzip2_main(int argc UNUSED_PARAM, char **argv) 360int bunzip2_main(int argc UNUSED_PARAM, char **argv)
334{ 361{
335 getopt32(argv, "cfvdt"); 362 getopt32(argv, "cfvqdt");
336 argv += optind; 363 argv += optind;
337 if (applet_name[2] == 'c') /* bzcat */ 364 if (applet_name[2] == 'c') /* bzcat */
338 option_mask32 |= OPT_STDOUT; 365 option_mask32 |= OPT_STDOUT;
@@ -367,7 +394,7 @@ int bunzip2_main(int argc UNUSED_PARAM, char **argv)
367//usage: "\n -f Force" 394//usage: "\n -f Force"
368//usage: 395//usage:
369//usage:#define lzcat_trivial_usage 396//usage:#define lzcat_trivial_usage
370//usage: "FILE" 397//usage: "[FILE]..."
371//usage:#define lzcat_full_usage "\n\n" 398//usage:#define lzcat_full_usage "\n\n"
372//usage: "Decompress to stdout" 399//usage: "Decompress to stdout"
373//usage: 400//usage:
@@ -387,7 +414,7 @@ int bunzip2_main(int argc UNUSED_PARAM, char **argv)
387//usage: "\n -f Force" 414//usage: "\n -f Force"
388//usage: 415//usage:
389//usage:#define xzcat_trivial_usage 416//usage:#define xzcat_trivial_usage
390//usage: "FILE" 417//usage: "[FILE]..."
391//usage:#define xzcat_full_usage "\n\n" 418//usage:#define xzcat_full_usage "\n\n"
392//usage: "Decompress to stdout" 419//usage: "Decompress to stdout"
393 420
@@ -400,7 +427,7 @@ IF_DESKTOP(long long) int FAST_FUNC unpack_unlzma(transformer_aux_data_t *aux)
400int unlzma_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 427int unlzma_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
401int unlzma_main(int argc UNUSED_PARAM, char **argv) 428int unlzma_main(int argc UNUSED_PARAM, char **argv)
402{ 429{
403 IF_LZMA(int opts =) getopt32(argv, "cfvdt"); 430 IF_LZMA(int opts =) getopt32(argv, "cfvqdt");
404# if ENABLE_LZMA 431# if ENABLE_LZMA
405 /* lzma without -d or -t? */ 432 /* lzma without -d or -t? */
406 if (applet_name[2] == 'm' && !(opts & (OPT_DECOMPRESS|OPT_TEST))) 433 if (applet_name[2] == 'm' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
@@ -425,7 +452,7 @@ IF_DESKTOP(long long) int FAST_FUNC unpack_unxz(transformer_aux_data_t *aux)
425int unxz_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 452int unxz_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
426int unxz_main(int argc UNUSED_PARAM, char **argv) 453int unxz_main(int argc UNUSED_PARAM, char **argv)
427{ 454{
428 IF_XZ(int opts =) getopt32(argv, "cfvdt"); 455 IF_XZ(int opts =) getopt32(argv, "cfvqdt");
429# if ENABLE_XZ 456# if ENABLE_XZ
430 /* xz without -d or -t? */ 457 /* xz without -d or -t? */
431 if (applet_name[2] == '\0' && !(opts & (OPT_DECOMPRESS|OPT_TEST))) 458 if (applet_name[2] == '\0' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
diff --git a/archival/libarchive/data_extract_all.c b/archival/libarchive/data_extract_all.c
index 3f67b835f..45776dcbe 100644
--- a/archival/libarchive/data_extract_all.c
+++ b/archival/libarchive/data_extract_all.c
@@ -106,15 +106,28 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
106 switch (file_header->mode & S_IFMT) { 106 switch (file_header->mode & S_IFMT) {
107 case S_IFREG: { 107 case S_IFREG: {
108 /* Regular file */ 108 /* Regular file */
109 char *dst_name;
109 int flags = O_WRONLY | O_CREAT | O_EXCL; 110 int flags = O_WRONLY | O_CREAT | O_EXCL;
110 if (archive_handle->ah_flags & ARCHIVE_O_TRUNC) 111 if (archive_handle->ah_flags & ARCHIVE_O_TRUNC)
111 flags = O_WRONLY | O_CREAT | O_TRUNC; 112 flags = O_WRONLY | O_CREAT | O_TRUNC;
112 dst_fd = xopen3(file_header->name, 113 dst_name = file_header->name;
114#ifdef ARCHIVE_REPLACE_VIA_RENAME
115 if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME)
116 /* rpm-style temp file name */
117 dst_name = xasprintf("%s;%x", dst_name, (int)getpid());
118#endif
119 dst_fd = xopen3(dst_name,
113 flags, 120 flags,
114 file_header->mode 121 file_header->mode
115 ); 122 );
116 bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size); 123 bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size);
117 close(dst_fd); 124 close(dst_fd);
125#ifdef ARCHIVE_REPLACE_VIA_RENAME
126 if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME) {
127 xrename(dst_name, file_header->name);
128 free(dst_name);
129 }
130#endif
118 break; 131 break;
119 } 132 }
120 case S_IFDIR: 133 case S_IFDIR:
diff --git a/archival/libarchive/decompress_unlzma.c b/archival/libarchive/decompress_unlzma.c
index cfde8ea56..ca32bd82c 100644
--- a/archival/libarchive/decompress_unlzma.c
+++ b/archival/libarchive/decompress_unlzma.c
@@ -45,16 +45,16 @@ typedef struct {
45#define RC_MODEL_TOTAL_BITS 11 45#define RC_MODEL_TOTAL_BITS 11
46 46
47 47
48/* Called twice: once at startup (LZMA_FAST only) and once in rc_normalize() */ 48/* Called once in rc_do_normalize() */
49static size_inline void rc_read(rc_t *rc) 49static void rc_read(rc_t *rc)
50{ 50{
51 int buffer_size = safe_read(rc->fd, RC_BUFFER, RC_BUFFER_SIZE); 51 int buffer_size = safe_read(rc->fd, RC_BUFFER, RC_BUFFER_SIZE);
52//TODO: return -1 instead 52//TODO: return -1 instead
53//This will make unlzma delete broken unpacked file on unpack errors 53//This will make unlzma delete broken unpacked file on unpack errors
54 if (buffer_size <= 0) 54 if (buffer_size <= 0)
55 bb_error_msg_and_die("unexpected EOF"); 55 bb_error_msg_and_die("unexpected EOF");
56 rc->ptr = RC_BUFFER;
57 rc->buffer_end = RC_BUFFER + buffer_size; 56 rc->buffer_end = RC_BUFFER + buffer_size;
57 rc->ptr = RC_BUFFER;
58} 58}
59 59
60/* Called twice, but one callsite is in speed_inline'd rc_is_bit_1() */ 60/* Called twice, but one callsite is in speed_inline'd rc_is_bit_1() */
@@ -65,6 +65,12 @@ static void rc_do_normalize(rc_t *rc)
65 rc->range <<= 8; 65 rc->range <<= 8;
66 rc->code = (rc->code << 8) | *rc->ptr++; 66 rc->code = (rc->code << 8) | *rc->ptr++;
67} 67}
68static ALWAYS_INLINE void rc_normalize(rc_t *rc)
69{
70 if (rc->range < (1 << RC_TOP_BITS)) {
71 rc_do_normalize(rc);
72 }
73}
68 74
69/* Called once */ 75/* Called once */
70static ALWAYS_INLINE rc_t* rc_init(int fd) /*, int buffer_size) */ 76static ALWAYS_INLINE rc_t* rc_init(int fd) /*, int buffer_size) */
@@ -78,15 +84,9 @@ static ALWAYS_INLINE rc_t* rc_init(int fd) /*, int buffer_size) */
78 /* rc->ptr = rc->buffer_end; */ 84 /* rc->ptr = rc->buffer_end; */
79 85
80 for (i = 0; i < 5; i++) { 86 for (i = 0; i < 5; i++) {
81#if ENABLE_FEATURE_LZMA_FAST
82 if (rc->ptr >= rc->buffer_end)
83 rc_read(rc);
84 rc->code = (rc->code << 8) | *rc->ptr++;
85#else
86 rc_do_normalize(rc); 87 rc_do_normalize(rc);
87#endif
88 } 88 }
89 rc->range = 0xFFFFFFFF; 89 rc->range = 0xffffffff;
90 return rc; 90 return rc;
91} 91}
92 92
@@ -96,13 +96,6 @@ static ALWAYS_INLINE void rc_free(rc_t *rc)
96 free(rc); 96 free(rc);
97} 97}
98 98
99static ALWAYS_INLINE void rc_normalize(rc_t *rc)
100{
101 if (rc->range < (1 << RC_TOP_BITS)) {
102 rc_do_normalize(rc);
103 }
104}
105
106/* rc_is_bit_1 is called 9 times */ 99/* rc_is_bit_1 is called 9 times */
107static speed_inline int rc_is_bit_1(rc_t *rc, uint16_t *p) 100static speed_inline int rc_is_bit_1(rc_t *rc, uint16_t *p)
108{ 101{
@@ -120,7 +113,7 @@ static speed_inline int rc_is_bit_1(rc_t *rc, uint16_t *p)
120} 113}
121 114
122/* Called 4 times in unlzma loop */ 115/* Called 4 times in unlzma loop */
123static speed_inline int rc_get_bit(rc_t *rc, uint16_t *p, int *symbol) 116static ALWAYS_INLINE int rc_get_bit(rc_t *rc, uint16_t *p, int *symbol)
124{ 117{
125 int ret = rc_is_bit_1(rc, p); 118 int ret = rc_is_bit_1(rc, p);
126 *symbol = *symbol * 2 + ret; 119 *symbol = *symbol * 2 + ret;
diff --git a/archival/libarchive/decompress_unxz.c b/archival/libarchive/decompress_unxz.c
index 79b48a152..986b7b191 100644
--- a/archival/libarchive/decompress_unxz.c
+++ b/archival/libarchive/decompress_unxz.c
@@ -30,8 +30,8 @@ static uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)
30/* We use arch-optimized unaligned accessors */ 30/* We use arch-optimized unaligned accessors */
31#define get_unaligned_le32(buf) ({ uint32_t v; move_from_unaligned32(v, buf); SWAP_LE32(v); }) 31#define get_unaligned_le32(buf) ({ uint32_t v; move_from_unaligned32(v, buf); SWAP_LE32(v); })
32#define get_unaligned_be32(buf) ({ uint32_t v; move_from_unaligned32(v, buf); SWAP_BE32(v); }) 32#define get_unaligned_be32(buf) ({ uint32_t v; move_from_unaligned32(v, buf); SWAP_BE32(v); })
33#define put_unaligned_le32(val, buf) move_to_unaligned16(buf, SWAP_LE32(val)) 33#define put_unaligned_le32(val, buf) move_to_unaligned32(buf, SWAP_LE32(val))
34#define put_unaligned_be32(val, buf) move_to_unaligned16(buf, SWAP_BE32(val)) 34#define put_unaligned_be32(val, buf) move_to_unaligned32(buf, SWAP_BE32(val))
35 35
36#include "unxz/xz_dec_bcj.c" 36#include "unxz/xz_dec_bcj.c"
37#include "unxz/xz_dec_lzma2.c" 37#include "unxz/xz_dec_lzma2.c"
@@ -40,6 +40,7 @@ static uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)
40IF_DESKTOP(long long) int FAST_FUNC 40IF_DESKTOP(long long) int FAST_FUNC
41unpack_xz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd) 41unpack_xz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
42{ 42{
43 enum xz_ret xz_result;
43 struct xz_buf iobuf; 44 struct xz_buf iobuf;
44 struct xz_dec *state; 45 struct xz_dec *state;
45 unsigned char *membuf; 46 unsigned char *membuf;
@@ -63,9 +64,8 @@ unpack_xz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
63 /* Limit memory usage to about 64 MiB. */ 64 /* Limit memory usage to about 64 MiB. */
64 state = xz_dec_init(XZ_DYNALLOC, 64*1024*1024); 65 state = xz_dec_init(XZ_DYNALLOC, 64*1024*1024);
65 66
67 xz_result = X_OK;
66 while (1) { 68 while (1) {
67 enum xz_ret r;
68
69 if (iobuf.in_pos == iobuf.in_size) { 69 if (iobuf.in_pos == iobuf.in_size) {
70 int rd = safe_read(src_fd, membuf, BUFSIZ); 70 int rd = safe_read(src_fd, membuf, BUFSIZ);
71 if (rd < 0) { 71 if (rd < 0) {
@@ -73,28 +73,57 @@ unpack_xz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
73 total = -1; 73 total = -1;
74 break; 74 break;
75 } 75 }
76 if (rd == 0 && xz_result == XZ_STREAM_END)
77 break;
76 iobuf.in_size = rd; 78 iobuf.in_size = rd;
77 iobuf.in_pos = 0; 79 iobuf.in_pos = 0;
78 } 80 }
81 if (xz_result == XZ_STREAM_END) {
82 /*
83 * Try to start decoding next concatenated stream.
84 * Stream padding must always be a multiple of four
85 * bytes to preserve four-byte alignment. To keep the
86 * code slightly smaller, we aren't as strict here as
87 * the .xz spec requires. We just skip all zero-bytes
88 * without checking the alignment and thus can accept
89 * files that aren't valid, e.g. the XZ utils test
90 * files bad-0pad-empty.xz and bad-0catpad-empty.xz.
91 */
92 do {
93 if (membuf[iobuf.in_pos] != 0) {
94 xz_dec_reset(state);
95 goto do_run;
96 }
97 iobuf.in_pos++;
98 } while (iobuf.in_pos < iobuf.in_size);
99 }
100 do_run:
79// bb_error_msg(">in pos:%d size:%d out pos:%d size:%d", 101// bb_error_msg(">in pos:%d size:%d out pos:%d size:%d",
80// iobuf.in_pos, iobuf.in_size, iobuf.out_pos, iobuf.out_size); 102// iobuf.in_pos, iobuf.in_size, iobuf.out_pos, iobuf.out_size);
81 r = xz_dec_run(state, &iobuf); 103 xz_result = xz_dec_run(state, &iobuf);
82// bb_error_msg("<in pos:%d size:%d out pos:%d size:%d r:%d", 104// bb_error_msg("<in pos:%d size:%d out pos:%d size:%d r:%d",
83// iobuf.in_pos, iobuf.in_size, iobuf.out_pos, iobuf.out_size, r); 105// iobuf.in_pos, iobuf.in_size, iobuf.out_pos, iobuf.out_size, xz_result);
84 if (iobuf.out_pos) { 106 if (iobuf.out_pos) {
85 xwrite(dst_fd, iobuf.out, iobuf.out_pos); 107 xwrite(dst_fd, iobuf.out, iobuf.out_pos);
86 IF_DESKTOP(total += iobuf.out_pos;) 108 IF_DESKTOP(total += iobuf.out_pos;)
87 iobuf.out_pos = 0; 109 iobuf.out_pos = 0;
88 } 110 }
89 if (r == XZ_STREAM_END) { 111 if (xz_result == XZ_STREAM_END) {
90 break; 112 /*
113 * Can just "break;" here, if not for concatenated
114 * .xz streams.
115 * Checking for padding may require buffer
116 * replenishment. Can't do it here.
117 */
118 continue;
91 } 119 }
92 if (r != XZ_OK && r != XZ_UNSUPPORTED_CHECK) { 120 if (xz_result != XZ_OK && xz_result != XZ_UNSUPPORTED_CHECK) {
93 bb_error_msg("corrupted data"); 121 bb_error_msg("corrupted data");
94 total = -1; 122 total = -1;
95 break; 123 break;
96 } 124 }
97 } 125 }
126
98 xz_dec_end(state); 127 xz_dec_end(state);
99 free(membuf); 128 free(membuf);
100 129
diff --git a/archival/libarchive/open_transformer.c b/archival/libarchive/open_transformer.c
index f2edc2a2b..841e9dce9 100644
--- a/archival/libarchive/open_transformer.c
+++ b/archival/libarchive/open_transformer.c
@@ -35,6 +35,7 @@ void check_errors_in_children(int signo)
35 if (!signo) { 35 if (!signo) {
36 /* block waiting for any child */ 36 /* block waiting for any child */
37 if (wait(&status) < 0) 37 if (wait(&status) < 0)
38//FIXME: check EINTR?
38 return; /* probably there are no children */ 39 return; /* probably there are no children */
39 goto check_status; 40 goto check_status;
40 } 41 }
@@ -42,14 +43,18 @@ void check_errors_in_children(int signo)
42 /* Wait for any child without blocking */ 43 /* Wait for any child without blocking */
43 for (;;) { 44 for (;;) {
44 if (wait_any_nohang(&status) < 0) 45 if (wait_any_nohang(&status) < 0)
46//FIXME: check EINTR?
45 /* wait failed?! I'm confused... */ 47 /* wait failed?! I'm confused... */
46 return; 48 return;
47 check_status: 49 check_status:
48 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 50 /*if (WIFEXITED(status) && WEXITSTATUS(status) == 0)*/
51 /* On Linux, the above can be checked simply as: */
52 if (status == 0)
49 /* this child exited with 0 */ 53 /* this child exited with 0 */
50 continue; 54 continue;
51 /* Cannot happen? 55 /* Cannot happen:
52 if (!WIFSIGNALED(status) && !WIFEXITED(status)) ???; */ 56 if (!WIFSIGNALED(status) && !WIFEXITED(status)) ???;
57 */
53 bb_got_signal = 1; 58 bb_got_signal = 1;
54 } 59 }
55} 60}
diff --git a/archival/libarchive/unxz/README b/archival/libarchive/unxz/README
index c5972f6b8..a84912035 100644
--- a/archival/libarchive/unxz/README
+++ b/archival/libarchive/unxz/README
@@ -7,7 +7,7 @@ XZ Embedded
7 7
8 XZ Embedded was written for use in the Linux kernel, but the code can 8 XZ Embedded was written for use in the Linux kernel, but the code can
9 be easily used in other environments too, including regular userspace 9 be easily used in other environments too, including regular userspace
10 applications. 10 applications. See userspace/xzminidec.c for an example program.
11 11
12 This README contains information that is useful only when the copy 12 This README contains information that is useful only when the copy
13 of XZ Embedded isn't part of the Linux kernel tree. You should also 13 of XZ Embedded isn't part of the Linux kernel tree. You should also
@@ -55,7 +55,7 @@ Compiler requirements
55 code is modified not to support large files, which needs some more 55 code is modified not to support large files, which needs some more
56 care than just using 32-bit integer instead of 64-bit). 56 care than just using 32-bit integer instead of 64-bit).
57 57
58 If you use GCC, try to use a recent version. For example, on x86, 58 If you use GCC, try to use a recent version. For example, on x86-32,
59 xz_dec_lzma2.c compiled with GCC 3.3.6 is 15-25 % slower than when 59 xz_dec_lzma2.c compiled with GCC 3.3.6 is 15-25 % slower than when
60 compiled with GCC 4.3.3. 60 compiled with GCC 4.3.3.
61 61
@@ -93,7 +93,7 @@ BCJ filter support
93 them always #defined doesn't hurt either. 93 them always #defined doesn't hurt either.
94 94
95 #define Instruction set BCJ filter endianness 95 #define Instruction set BCJ filter endianness
96 XZ_DEC_X86 x86 or x86-64 Little endian only 96 XZ_DEC_X86 x86-32 or x86-64 Little endian only
97 XZ_DEC_POWERPC PowerPC Big endian only 97 XZ_DEC_POWERPC PowerPC Big endian only
98 XZ_DEC_IA64 Itanium (IA-64) Big or little endian 98 XZ_DEC_IA64 Itanium (IA-64) Big or little endian
99 XZ_DEC_ARM ARM Little endian only 99 XZ_DEC_ARM ARM Little endian only
diff --git a/archival/libarchive/unxz/xz.h b/archival/libarchive/unxz/xz.h
index c6c071c4a..e0b22db56 100644
--- a/archival/libarchive/unxz/xz.h
+++ b/archival/libarchive/unxz/xz.h
@@ -19,6 +19,10 @@
19# include <stdint.h> 19# include <stdint.h>
20#endif 20#endif
21 21
22#ifdef __cplusplus
23extern "C" {
24#endif
25
22/* In Linux, this is used to make extern functions static when needed. */ 26/* In Linux, this is used to make extern functions static when needed. */
23#ifndef XZ_EXTERN 27#ifndef XZ_EXTERN
24# define XZ_EXTERN extern 28# define XZ_EXTERN extern
@@ -70,7 +74,7 @@ enum xz_mode {
70 * @XZ_UNSUPPORTED_CHECK: Integrity check type is not supported. Decoding 74 * @XZ_UNSUPPORTED_CHECK: Integrity check type is not supported. Decoding
71 * is still possible in multi-call mode by simply 75 * is still possible in multi-call mode by simply
72 * calling xz_dec_run() again. 76 * calling xz_dec_run() again.
73 * NOTE: This return value is used only if 77 * Note that this return value is used only if
74 * XZ_DEC_ANY_CHECK was defined at build time, 78 * XZ_DEC_ANY_CHECK was defined at build time,
75 * which is not used in the kernel. Unsupported 79 * which is not used in the kernel. Unsupported
76 * check types return XZ_OPTIONS_ERROR if 80 * check types return XZ_OPTIONS_ERROR if
@@ -105,7 +109,7 @@ enum xz_mode {
105 * stream that is truncated or otherwise corrupt. 109 * stream that is truncated or otherwise corrupt.
106 * 110 *
107 * In single-call mode, XZ_BUF_ERROR is returned only when the output buffer 111 * In single-call mode, XZ_BUF_ERROR is returned only when the output buffer
108 * is too small, or the compressed input is corrupt in a way that makes the 112 * is too small or the compressed input is corrupt in a way that makes the
109 * decoder produce more output than the caller expected. When it is 113 * decoder produce more output than the caller expected. When it is
110 * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR 114 * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR
111 * is used instead of XZ_BUF_ERROR. 115 * is used instead of XZ_BUF_ERROR.
@@ -207,8 +211,8 @@ XZ_EXTERN struct xz_dec * XZ_FUNC xz_dec_init(
207 * The possible return values depend on build options and operation mode. 211 * The possible return values depend on build options and operation mode.
208 * See enum xz_ret for details. 212 * See enum xz_ret for details.
209 * 213 *
210 * NOTE: If an error occurs in single-call mode (return value is not 214 * Note that if an error occurs in single-call mode (return value is not
211 * XZ_STREAM_END), b->in_pos and b->out_pos are not modified, and the 215 * XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the
212 * contents of the output buffer from b->out[b->out_pos] onward are 216 * contents of the output buffer from b->out[b->out_pos] onward are
213 * undefined. This is true even after XZ_BUF_ERROR, because with some filter 217 * undefined. This is true even after XZ_BUF_ERROR, because with some filter
214 * chains, there may be a second pass over the output buffer, and this pass 218 * chains, there may be a second pass over the output buffer, and this pass
@@ -268,4 +272,9 @@ XZ_EXTERN void XZ_FUNC xz_crc32_init(void);
268XZ_EXTERN uint32_t XZ_FUNC xz_crc32( 272XZ_EXTERN uint32_t XZ_FUNC xz_crc32(
269 const uint8_t *buf, size_t size, uint32_t crc); 273 const uint8_t *buf, size_t size, uint32_t crc);
270#endif 274#endif
275
276#ifdef __cplusplus
277}
278#endif
279
271#endif 280#endif
diff --git a/archival/libarchive/unxz/xz_dec_bcj.c b/archival/libarchive/unxz/xz_dec_bcj.c
index 09162b51f..e0f913a94 100644
--- a/archival/libarchive/unxz/xz_dec_bcj.c
+++ b/archival/libarchive/unxz/xz_dec_bcj.c
@@ -77,10 +77,13 @@ struct xz_dec_bcj {
77 77
78#ifdef XZ_DEC_X86 78#ifdef XZ_DEC_X86
79/* 79/*
80 * This is macro used to test the most significant byte of a memory address 80 * This is used to test the most significant byte of a memory address
81 * in an x86 instruction. 81 * in an x86 instruction.
82 */ 82 */
83#define bcj_x86_test_msbyte(b) ((b) == 0x00 || (b) == 0xFF) 83static inline int bcj_x86_test_msbyte(uint8_t b)
84{
85 return b == 0x00 || b == 0xFF;
86}
84 87
85static noinline_for_stack size_t XZ_FUNC bcj_x86( 88static noinline_for_stack size_t XZ_FUNC bcj_x86(
86 struct xz_dec_bcj *s, uint8_t *buf, size_t size) 89 struct xz_dec_bcj *s, uint8_t *buf, size_t size)
@@ -443,8 +446,12 @@ XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_bcj_run(struct xz_dec_bcj *s,
443 * next filter in the chain. Apply the BCJ filter on the new data 446 * next filter in the chain. Apply the BCJ filter on the new data
444 * in the output buffer. If everything cannot be filtered, copy it 447 * in the output buffer. If everything cannot be filtered, copy it
445 * to temp and rewind the output buffer position accordingly. 448 * to temp and rewind the output buffer position accordingly.
449 *
450 * This needs to be always run when temp.size == 0 to handle a special
451 * case where the output buffer is full and the next filter has no
452 * more output coming but hasn't returned XZ_STREAM_END yet.
446 */ 453 */
447 if (s->temp.size < b->out_size - b->out_pos) { 454 if (s->temp.size < b->out_size - b->out_pos || s->temp.size == 0) {
448 out_start = b->out_pos; 455 out_start = b->out_pos;
449 memcpy(b->out + b->out_pos, s->temp.buf, s->temp.size); 456 memcpy(b->out + b->out_pos, s->temp.buf, s->temp.size);
450 b->out_pos += s->temp.size; 457 b->out_pos += s->temp.size;
@@ -467,16 +474,25 @@ XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_bcj_run(struct xz_dec_bcj *s,
467 s->temp.size = b->out_pos - out_start; 474 s->temp.size = b->out_pos - out_start;
468 b->out_pos -= s->temp.size; 475 b->out_pos -= s->temp.size;
469 memcpy(s->temp.buf, b->out + b->out_pos, s->temp.size); 476 memcpy(s->temp.buf, b->out + b->out_pos, s->temp.size);
477
478 /*
479 * If there wasn't enough input to the next filter to fill
480 * the output buffer with unfiltered data, there's no point
481 * to try decoding more data to temp.
482 */
483 if (b->out_pos + s->temp.size < b->out_size)
484 return XZ_OK;
470 } 485 }
471 486
472 /* 487 /*
473 * If we have unfiltered data in temp, try to fill by decoding more 488 * We have unfiltered data in temp. If the output buffer isn't full
474 * data from the next filter. Apply the BCJ filter on temp. Then we 489 * yet, try to fill the temp buffer by decoding more data from the
475 * hopefully can fill the actual output buffer by copying filtered 490 * next filter. Apply the BCJ filter on temp. Then we hopefully can
476 * data from temp. A mix of filtered and unfiltered data may be left 491 * fill the actual output buffer by copying filtered data from temp.
477 * in temp; it will be taken care on the next call to this function. 492 * A mix of filtered and unfiltered data may be left in temp; it will
493 * be taken care on the next call to this function.
478 */ 494 */
479 if (s->temp.size > 0) { 495 if (b->out_pos < b->out_size) {
480 /* Make b->out{,_pos,_size} temporarily point to s->temp. */ 496 /* Make b->out{,_pos,_size} temporarily point to s->temp. */
481 s->out = b->out; 497 s->out = b->out;
482 s->out_pos = b->out_pos; 498 s->out_pos = b->out_pos;
diff --git a/archival/libarchive/unxz/xz_dec_lzma2.c b/archival/libarchive/unxz/xz_dec_lzma2.c
index da71cb4d4..3c2dc88b7 100644
--- a/archival/libarchive/unxz/xz_dec_lzma2.c
+++ b/archival/libarchive/unxz/xz_dec_lzma2.c
@@ -407,7 +407,6 @@ static void XZ_FUNC dict_uncompressed(
407 407
408 b->out_pos += copy_size; 408 b->out_pos += copy_size;
409 b->in_pos += copy_size; 409 b->in_pos += copy_size;
410
411 } 410 }
412} 411}
413 412
@@ -972,6 +971,9 @@ XZ_EXTERN NOINLINE enum xz_ret XZ_FUNC xz_dec_lzma2_run(
972 */ 971 */
973 tmp = b->in[b->in_pos++]; 972 tmp = b->in[b->in_pos++];
974 973
974 if (tmp == 0x00)
975 return XZ_STREAM_END;
976
975 if (tmp >= 0xE0 || tmp == 0x01) { 977 if (tmp >= 0xE0 || tmp == 0x01) {
976 s->lzma2.need_props = true; 978 s->lzma2.need_props = true;
977 s->lzma2.need_dict_reset = false; 979 s->lzma2.need_dict_reset = false;
@@ -1004,9 +1006,6 @@ XZ_EXTERN NOINLINE enum xz_ret XZ_FUNC xz_dec_lzma2_run(
1004 lzma_reset(s); 1006 lzma_reset(s);
1005 } 1007 }
1006 } else { 1008 } else {
1007 if (tmp == 0x00)
1008 return XZ_STREAM_END;
1009
1010 if (tmp > 0x02) 1009 if (tmp > 0x02)
1011 return XZ_DATA_ERROR; 1010 return XZ_DATA_ERROR;
1012 1011
diff --git a/archival/libarchive/unxz/xz_stream.h b/archival/libarchive/unxz/xz_stream.h
index 36f2a7cbf..66cb5a705 100644
--- a/archival/libarchive/unxz/xz_stream.h
+++ b/archival/libarchive/unxz/xz_stream.h
@@ -25,15 +25,20 @@
25 25
26#define STREAM_HEADER_SIZE 12 26#define STREAM_HEADER_SIZE 12
27 27
28#define HEADER_MAGIC "\3757zXZ\0" 28#define HEADER_MAGIC "\3757zXZ"
29#define HEADER_MAGIC_SIZE 6 29#define HEADER_MAGIC_SIZE 6
30 30
31#define FOOTER_MAGIC "YZ" 31#define FOOTER_MAGIC "YZ"
32#define FOOTER_MAGIC_SIZE 2 32#define FOOTER_MAGIC_SIZE 2
33 33
34/* 34/*
35 * Variable-length integer can hold a 63-bit unsigned integer, or a special 35 * Variable-length integer can hold a 63-bit unsigned integer or a special
36 * value to indicate that the value is unknown. 36 * value indicating that the value is unknown.
37 *
38 * Experimental: vli_type can be defined to uint32_t to save a few bytes
39 * in code size (no effect on speed). Doing so limits the uncompressed and
40 * compressed size of the file to less than 256 MiB and may also weaken
41 * error detection slightly.
37 */ 42 */
38typedef uint64_t vli_type; 43typedef uint64_t vli_type;
39 44
diff --git a/archival/lzop.c b/archival/lzop.c
index 56003d421..9b42e5fd3 100644
--- a/archival/lzop.c
+++ b/archival/lzop.c
@@ -436,25 +436,27 @@ struct globals {
436//#define LZOP_VERSION_STRING "1.01" 436//#define LZOP_VERSION_STRING "1.01"
437//#define LZOP_VERSION_DATE "Apr 27th 2003" 437//#define LZOP_VERSION_DATE "Apr 27th 2003"
438 438
439#define OPTION_STRING "cfvdt123456789CF" 439#define OPTION_STRING "cfvqdt123456789CF"
440 440
441/* Note: must be kept in sync with archival/bbunzip.c */
441enum { 442enum {
442 OPT_STDOUT = (1 << 0), 443 OPT_STDOUT = (1 << 0),
443 OPT_FORCE = (1 << 1), 444 OPT_FORCE = (1 << 1),
444 OPT_VERBOSE = (1 << 2), 445 OPT_VERBOSE = (1 << 2),
445 OPT_DECOMPRESS = (1 << 3), 446 OPT_QUIET = (1 << 3),
446 OPT_TEST = (1 << 4), 447 OPT_DECOMPRESS = (1 << 4),
447 OPT_1 = (1 << 5), 448 OPT_TEST = (1 << 5),
448 OPT_2 = (1 << 6), 449 OPT_1 = (1 << 6),
449 OPT_3 = (1 << 7), 450 OPT_2 = (1 << 7),
450 OPT_4 = (1 << 8), 451 OPT_3 = (1 << 8),
451 OPT_5 = (1 << 9), 452 OPT_4 = (1 << 9),
452 OPT_6 = (1 << 10), 453 OPT_5 = (1 << 10),
453 OPT_789 = (7 << 11), 454 OPT_6 = (1 << 11),
454 OPT_7 = (1 << 11), 455 OPT_789 = (7 << 12),
455 OPT_8 = (1 << 12), 456 OPT_7 = (1 << 13),
456 OPT_C = (1 << 14), 457 OPT_8 = (1 << 14),
457 OPT_F = (1 << 15), 458 OPT_C = (1 << 15),
459 OPT_F = (1 << 16),
458}; 460};
459 461
460/**********************************************************************/ 462/**********************************************************************/
@@ -1093,7 +1095,7 @@ int lzop_main(int argc UNUSED_PARAM, char **argv)
1093 if (applet_name[4] == 'c') 1095 if (applet_name[4] == 'c')
1094 option_mask32 |= (OPT_STDOUT | OPT_DECOMPRESS); 1096 option_mask32 |= (OPT_STDOUT | OPT_DECOMPRESS);
1095 /* unlzop? */ 1097 /* unlzop? */
1096 if (applet_name[0] == 'u') 1098 if (applet_name[4] == 'o')
1097 option_mask32 |= OPT_DECOMPRESS; 1099 option_mask32 |= OPT_DECOMPRESS;
1098 1100
1099 global_crc32_table = crc32_filltable(NULL, 0); 1101 global_crc32_table = crc32_filltable(NULL, 0);
diff --git a/archival/rpm.c b/archival/rpm.c
index 6757a6ceb..86ba4dca4 100644
--- a/archival/rpm.c
+++ b/archival/rpm.c
@@ -14,10 +14,10 @@
14//usage: "\nCommands:" 14//usage: "\nCommands:"
15//usage: "\n -i Install package" 15//usage: "\n -i Install package"
16//usage: "\n -qp Query package" 16//usage: "\n -qp Query package"
17//usage: "\n -i Show information" 17//usage: "\n -qpi Show information"
18//usage: "\n -l List contents" 18//usage: "\n -qpl List contents"
19//usage: "\n -d List documents" 19//usage: "\n -qpd List documents"
20//usage: "\n -c List config files" 20//usage: "\n -qpc List config files"
21 21
22#include "libbb.h" 22#include "libbb.h"
23#include "bb_archive.h" 23#include "bb_archive.h"
@@ -79,136 +79,13 @@ typedef struct {
79 uint32_t count; /* 4 byte count */ 79 uint32_t count; /* 4 byte count */
80} rpm_index; 80} rpm_index;
81 81
82static void *map; 82struct globals {
83static rpm_index **mytags; 83 void *map;
84static int tagcount; 84 rpm_index **mytags;
85 85 int tagcount;
86static void extract_cpio(int fd, const char *source_rpm); 86} FIX_ALIASING;
87static rpm_index **rpm_gettags(int fd, int *num_tags); 87#define G (*(struct globals*)&bb_common_bufsiz1)
88static int bsearch_rpmtag(const void *key, const void *item); 88#define INIT_G() do { } while (0)
89static char *rpm_getstr(int tag, int itemindex);
90static int rpm_getint(int tag, int itemindex);
91static int rpm_getcount(int tag);
92static void fileaction_dobackup(char *filename, int fileref);
93static void fileaction_setowngrp(char *filename, int fileref);
94static void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref));
95
96int rpm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
97int rpm_main(int argc, char **argv)
98{
99 int opt = 0, func = 0, rpm_fd, offset;
100 const int pagesize = getpagesize();
101
102 while ((opt = getopt(argc, argv, "iqpldc")) != -1) {
103 switch (opt) {
104 case 'i': /* First arg: Install mode, with q: Information */
105 if (!func) func = rpm_install;
106 else func |= rpm_query_info;
107 break;
108 case 'q': /* First arg: Query mode */
109 if (func) bb_show_usage();
110 func = rpm_query;
111 break;
112 case 'p': /* Query a package */
113 func |= rpm_query_package;
114 break;
115 case 'l': /* List files in a package */
116 func |= rpm_query_list;
117 break;
118 case 'd': /* List doc files in a package (implies list) */
119 func |= rpm_query_list;
120 func |= rpm_query_list_doc;
121 break;
122 case 'c': /* List config files in a package (implies list) */
123 func |= rpm_query_list;
124 func |= rpm_query_list_config;
125 break;
126 default:
127 bb_show_usage();
128 }
129 }
130 argv += optind;
131 //argc -= optind;
132 if (!argv[0]) {
133 bb_show_usage();
134 }
135
136 while (*argv) {
137 const char *source_rpm;
138
139 rpm_fd = xopen(*argv++, O_RDONLY);
140 mytags = rpm_gettags(rpm_fd, &tagcount);
141 if (!mytags)
142 bb_error_msg_and_die("error reading rpm header");
143 offset = xlseek(rpm_fd, 0, SEEK_CUR);
144 /* Mimimum is one page */
145 map = mmap(0, offset > pagesize ? (offset + offset % pagesize) : pagesize, PROT_READ, MAP_PRIVATE, rpm_fd, 0);
146
147 source_rpm = rpm_getstr(TAG_SOURCERPM, 0);
148
149 if (func & rpm_install) {
150 /* Backup any config files */
151 loop_through_files(TAG_BASENAMES, fileaction_dobackup);
152 /* Extact the archive */
153 extract_cpio(rpm_fd, source_rpm);
154 /* Set the correct file uid/gid's */
155 loop_through_files(TAG_BASENAMES, fileaction_setowngrp);
156 }
157 else if ((func & (rpm_query|rpm_query_package)) == (rpm_query|rpm_query_package)) {
158 if (!(func & (rpm_query_info|rpm_query_list))) {
159 /* If just a straight query, just give package name */
160 printf("%s-%s-%s\n", rpm_getstr(TAG_NAME, 0), rpm_getstr(TAG_VERSION, 0), rpm_getstr(TAG_RELEASE, 0));
161 }
162 if (func & rpm_query_info) {
163 /* Do the nice printout */
164 time_t bdate_time;
165 struct tm *bdate_ptm;
166 char bdatestring[50];
167 const char *p;
168
169 p = rpm_getstr(TAG_PREFIXS, 0);
170 if (!p) p = "(not relocateable)";
171 printf("Name : %-29sRelocations: %s\n", rpm_getstr(TAG_NAME, 0), p);
172 p = rpm_getstr(TAG_VENDOR, 0);
173 if (!p) p = "(none)";
174 printf("Version : %-34sVendor: %s\n", rpm_getstr(TAG_VERSION, 0), p);
175 bdate_time = rpm_getint(TAG_BUILDTIME, 0);
176 bdate_ptm = localtime(&bdate_time);
177 strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate_ptm);
178 printf("Release : %-30sBuild Date: %s\n", rpm_getstr(TAG_RELEASE, 0), bdatestring);
179 printf("Install date: %-30sBuild Host: %s\n", "(not installed)", rpm_getstr(TAG_BUILDHOST, 0));
180 printf("Group : %-30sSource RPM: %s\n", rpm_getstr(TAG_GROUP, 0), source_rpm);
181 printf("Size : %-33dLicense: %s\n", rpm_getint(TAG_SIZE, 0), rpm_getstr(TAG_LICENSE, 0));
182 printf("URL : %s\n", rpm_getstr(TAG_URL, 0));
183 printf("Summary : %s\n", rpm_getstr(TAG_SUMMARY, 0));
184 printf("Description :\n%s\n", rpm_getstr(TAG_DESCRIPTION, 0));
185 }
186 if (func & rpm_query_list) {
187 int count, it, flags;
188 count = rpm_getcount(TAG_BASENAMES);
189 for (it = 0; it < count; it++) {
190 flags = rpm_getint(TAG_FILEFLAGS, it);
191 switch (func & (rpm_query_list_doc|rpm_query_list_config)) {
192 case rpm_query_list_doc:
193 if (!(flags & RPMFILE_DOC)) continue;
194 break;
195 case rpm_query_list_config:
196 if (!(flags & RPMFILE_CONFIG)) continue;
197 break;
198 case rpm_query_list_doc|rpm_query_list_config:
199 if (!(flags & (RPMFILE_CONFIG|RPMFILE_DOC))) continue;
200 break;
201 }
202 printf("%s%s\n",
203 rpm_getstr(TAG_DIRNAMES, rpm_getint(TAG_DIRINDEXES, it)),
204 rpm_getstr(TAG_BASENAMES, it));
205 }
206 }
207 }
208 free(mytags);
209 }
210 return 0;
211}
212 89
213static void extract_cpio(int fd, const char *source_rpm) 90static void extract_cpio(int fd, const char *source_rpm)
214{ 91{
@@ -231,8 +108,8 @@ static void extract_cpio(int fd, const char *source_rpm)
231 /* compat: overwrite existing files. 108 /* compat: overwrite existing files.
232 * try "rpm -i foo.src.rpm" few times in a row - 109 * try "rpm -i foo.src.rpm" few times in a row -
233 * standard rpm will not complain. 110 * standard rpm will not complain.
234 * (TODO? real rpm creates "file;1234" and then renames it) */ 111 */
235 | ARCHIVE_UNLINK_OLD; 112 | ARCHIVE_REPLACE_VIA_RENAME;
236 archive_handle->src_fd = fd; 113 archive_handle->src_fd = fd;
237 /*archive_handle->offset = 0; - init_handle() did it */ 114 /*archive_handle->offset = 0; - init_handle() did it */
238 115
@@ -294,7 +171,7 @@ static int bsearch_rpmtag(const void *key, const void *item)
294static int rpm_getcount(int tag) 171static int rpm_getcount(int tag)
295{ 172{
296 rpm_index **found; 173 rpm_index **found;
297 found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag); 174 found = bsearch(&tag, G.mytags, G.tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
298 if (!found) 175 if (!found)
299 return 0; 176 return 0;
300 return found[0]->count; 177 return found[0]->count;
@@ -303,7 +180,7 @@ static int rpm_getcount(int tag)
303static char *rpm_getstr(int tag, int itemindex) 180static char *rpm_getstr(int tag, int itemindex)
304{ 181{
305 rpm_index **found; 182 rpm_index **found;
306 found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag); 183 found = bsearch(&tag, G.mytags, G.tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
307 if (!found || itemindex >= found[0]->count) 184 if (!found || itemindex >= found[0]->count)
308 return NULL; 185 return NULL;
309 if (found[0]->type == RPM_STRING_TYPE 186 if (found[0]->type == RPM_STRING_TYPE
@@ -311,7 +188,7 @@ static char *rpm_getstr(int tag, int itemindex)
311 || found[0]->type == RPM_STRING_ARRAY_TYPE 188 || found[0]->type == RPM_STRING_ARRAY_TYPE
312 ) { 189 ) {
313 int n; 190 int n;
314 char *tmpstr = (char *) map + found[0]->offset; 191 char *tmpstr = (char *) G.map + found[0]->offset;
315 for (n = 0; n < itemindex; n++) 192 for (n = 0; n < itemindex; n++)
316 tmpstr = tmpstr + strlen(tmpstr) + 1; 193 tmpstr = tmpstr + strlen(tmpstr) + 1;
317 return tmpstr; 194 return tmpstr;
@@ -322,32 +199,25 @@ static char *rpm_getstr(int tag, int itemindex)
322static int rpm_getint(int tag, int itemindex) 199static int rpm_getint(int tag, int itemindex)
323{ 200{
324 rpm_index **found; 201 rpm_index **found;
325 int *tmpint; /* NB: using int8_t* would be easier to code */ 202 char *tmpint;
326 203
327 /* gcc throws warnings here when sizeof(void*)!=sizeof(int) ... 204 /* gcc throws warnings here when sizeof(void*)!=sizeof(int) ...
328 * it's ok to ignore it because tag won't be used as a pointer */ 205 * it's ok to ignore it because tag won't be used as a pointer */
329 found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag); 206 found = bsearch(&tag, G.mytags, G.tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
330 if (!found || itemindex >= found[0]->count) 207 if (!found || itemindex >= found[0]->count)
331 return -1; 208 return -1;
332 209
333 tmpint = (int *) ((char *) map + found[0]->offset); 210 tmpint = (char *) G.map + found[0]->offset;
334
335 if (found[0]->type == RPM_INT32_TYPE) { 211 if (found[0]->type == RPM_INT32_TYPE) {
336 tmpint = (int *) ((char *) tmpint + itemindex*4); 212 tmpint += itemindex*4;
337 /*return ntohl(*tmpint);*/
338 /* int can be != int32_t */
339 return ntohl(*(int32_t*)tmpint); 213 return ntohl(*(int32_t*)tmpint);
340 } 214 }
341 if (found[0]->type == RPM_INT16_TYPE) { 215 if (found[0]->type == RPM_INT16_TYPE) {
342 tmpint = (int *) ((char *) tmpint + itemindex*2); 216 tmpint += itemindex*2;
343 /* ??? read int, and THEN ntohs() it?? */
344 /*return ntohs(*tmpint);*/
345 return ntohs(*(int16_t*)tmpint); 217 return ntohs(*(int16_t*)tmpint);
346 } 218 }
347 if (found[0]->type == RPM_INT8_TYPE) { 219 if (found[0]->type == RPM_INT8_TYPE) {
348 tmpint = (int *) ((char *) tmpint + itemindex); 220 tmpint += itemindex;
349 /* ??? why we don't read byte here??? */
350 /*return ntohs(*tmpint);*/
351 return *(int8_t*)tmpint; 221 return *(int8_t*)tmpint;
352 } 222 }
353 return -1; 223 return -1;
@@ -392,3 +262,134 @@ static void loop_through_files(int filetag, void (*fileaction)(char *filename, i
392 free(filename); 262 free(filename);
393 } 263 }
394} 264}
265
266int rpm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
267int rpm_main(int argc, char **argv)
268{
269 int opt, func = 0;
270 const unsigned pagesize = getpagesize();
271
272 while ((opt = getopt(argc, argv, "iqpldc")) != -1) {
273 switch (opt) {
274 case 'i': /* First arg: Install mode, with q: Information */
275 if (!func) func = rpm_install;
276 else func |= rpm_query_info;
277 break;
278 case 'q': /* First arg: Query mode */
279 if (func) bb_show_usage();
280 func = rpm_query;
281 break;
282 case 'p': /* Query a package */
283 func |= rpm_query_package;
284 break;
285 case 'l': /* List files in a package */
286 func |= rpm_query_list;
287 break;
288 case 'd': /* List doc files in a package (implies list) */
289 func |= rpm_query_list;
290 func |= rpm_query_list_doc;
291 break;
292 case 'c': /* List config files in a package (implies list) */
293 func |= rpm_query_list;
294 func |= rpm_query_list_config;
295 break;
296 default:
297 bb_show_usage();
298 }
299 }
300 argv += optind;
301 //argc -= optind;
302 if (!argv[0]) {
303 bb_show_usage();
304 }
305
306 while (*argv) {
307 int rpm_fd;
308 unsigned mapsize;
309 const char *source_rpm;
310
311 rpm_fd = xopen(*argv++, O_RDONLY);
312 G.mytags = rpm_gettags(rpm_fd, &G.tagcount);
313 if (!G.mytags)
314 bb_error_msg_and_die("error reading rpm header");
315 mapsize = xlseek(rpm_fd, 0, SEEK_CUR);
316 mapsize = (mapsize + pagesize) & -(int)pagesize;
317 /* Some NOMMU systems prefer MAP_PRIVATE over MAP_SHARED */
318 G.map = mmap(0, mapsize, PROT_READ, MAP_PRIVATE, rpm_fd, 0);
319//FIXME: error check?
320
321 source_rpm = rpm_getstr(TAG_SOURCERPM, 0);
322
323 if (func & rpm_install) {
324 /* Backup any config files */
325 loop_through_files(TAG_BASENAMES, fileaction_dobackup);
326 /* Extact the archive */
327 extract_cpio(rpm_fd, source_rpm);
328 /* Set the correct file uid/gid's */
329 loop_through_files(TAG_BASENAMES, fileaction_setowngrp);
330 }
331 else if ((func & (rpm_query|rpm_query_package)) == (rpm_query|rpm_query_package)) {
332 if (!(func & (rpm_query_info|rpm_query_list))) {
333 /* If just a straight query, just give package name */
334 printf("%s-%s-%s\n", rpm_getstr(TAG_NAME, 0), rpm_getstr(TAG_VERSION, 0), rpm_getstr(TAG_RELEASE, 0));
335 }
336 if (func & rpm_query_info) {
337 /* Do the nice printout */
338 time_t bdate_time;
339 struct tm *bdate_ptm;
340 char bdatestring[50];
341 const char *p;
342
343 printf("%-12s: %s\n", "Name" , rpm_getstr(TAG_NAME, 0));
344 /* TODO compat: add "Epoch" here */
345 printf("%-12s: %s\n", "Version" , rpm_getstr(TAG_VERSION, 0));
346 printf("%-12s: %s\n", "Release" , rpm_getstr(TAG_RELEASE, 0));
347 /* add "Architecture" */
348 printf("%-12s: %s\n", "Install Date", "(not installed)");
349 printf("%-12s: %s\n", "Group" , rpm_getstr(TAG_GROUP, 0));
350 printf("%-12s: %d\n", "Size" , rpm_getint(TAG_SIZE, 0));
351 printf("%-12s: %s\n", "License" , rpm_getstr(TAG_LICENSE, 0));
352 /* add "Signature" */
353 printf("%-12s: %s\n", "Source RPM" , source_rpm ? source_rpm : "(none)");
354 bdate_time = rpm_getint(TAG_BUILDTIME, 0);
355 bdate_ptm = localtime(&bdate_time);
356 strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate_ptm);
357 printf("%-12s: %s\n", "Build Date" , bdatestring);
358 printf("%-12s: %s\n", "Build Host" , rpm_getstr(TAG_BUILDHOST, 0));
359 p = rpm_getstr(TAG_PREFIXS, 0);
360 printf("%-12s: %s\n", "Relocations" , p ? p : "(not relocatable)");
361 /* add "Packager" */
362 p = rpm_getstr(TAG_VENDOR, 0);
363 printf("%-12s: %s\n", "Vendor" , p ? p : "(none)");
364 printf("%-12s: %s\n", "URL" , rpm_getstr(TAG_URL, 0));
365 printf("%-12s: %s\n", "Summary" , rpm_getstr(TAG_SUMMARY, 0));
366 printf("Description :\n%s\n", rpm_getstr(TAG_DESCRIPTION, 0));
367 }
368 if (func & rpm_query_list) {
369 int count, it, flags;
370 count = rpm_getcount(TAG_BASENAMES);
371 for (it = 0; it < count; it++) {
372 flags = rpm_getint(TAG_FILEFLAGS, it);
373 switch (func & (rpm_query_list_doc|rpm_query_list_config)) {
374 case rpm_query_list_doc:
375 if (!(flags & RPMFILE_DOC)) continue;
376 break;
377 case rpm_query_list_config:
378 if (!(flags & RPMFILE_CONFIG)) continue;
379 break;
380 case rpm_query_list_doc|rpm_query_list_config:
381 if (!(flags & (RPMFILE_CONFIG|RPMFILE_DOC))) continue;
382 break;
383 }
384 printf("%s%s\n",
385 rpm_getstr(TAG_DIRNAMES, rpm_getint(TAG_DIRINDEXES, it)),
386 rpm_getstr(TAG_BASENAMES, it));
387 }
388 }
389 }
390 munmap(G.map, mapsize);
391 free(G.mytags);
392 close(rpm_fd);
393 }
394 return 0;
395}
diff --git a/coreutils/Kbuild.src b/coreutils/Kbuild.src
index b715b9c47..ec4ef7df2 100644
--- a/coreutils/Kbuild.src
+++ b/coreutils/Kbuild.src
@@ -35,7 +35,6 @@ lib-$(CONFIG_EXPAND) += expand.o
35lib-$(CONFIG_FALSE) += false.o 35lib-$(CONFIG_FALSE) += false.o
36lib-$(CONFIG_FOLD) += fold.o 36lib-$(CONFIG_FOLD) += fold.o
37lib-$(CONFIG_FSYNC) += fsync.o 37lib-$(CONFIG_FSYNC) += fsync.o
38lib-$(CONFIG_HEAD) += head.o
39lib-$(CONFIG_INSTALL) += install.o 38lib-$(CONFIG_INSTALL) += install.o
40#lib-$(CONFIG_LENGTH) += length.o 39#lib-$(CONFIG_LENGTH) += length.o
41lib-$(CONFIG_LN) += ln.o 40lib-$(CONFIG_LN) += ln.o
@@ -71,7 +70,6 @@ lib-$(CONFIG_STTY) += stty.o
71lib-$(CONFIG_SUM) += sum.o 70lib-$(CONFIG_SUM) += sum.o
72lib-$(CONFIG_SYNC) += sync.o 71lib-$(CONFIG_SYNC) += sync.o
73lib-$(CONFIG_TAC) += tac.o 72lib-$(CONFIG_TAC) += tac.o
74lib-$(CONFIG_TAIL) += tail.o
75lib-$(CONFIG_TEE) += tee.o 73lib-$(CONFIG_TEE) += tee.o
76lib-$(CONFIG_TRUE) += true.o 74lib-$(CONFIG_TRUE) += true.o
77lib-$(CONFIG_TTY) += tty.o 75lib-$(CONFIG_TTY) += tty.o
diff --git a/coreutils/head.c b/coreutils/head.c
index ec4512765..291e1ce37 100644
--- a/coreutils/head.c
+++ b/coreutils/head.c
@@ -11,6 +11,9 @@
11/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */ 11/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
12/* http://www.opengroup.org/onlinepubs/007904975/utilities/head.html */ 12/* http://www.opengroup.org/onlinepubs/007904975/utilities/head.html */
13 13
14//kbuild:lib-$(CONFIG_HEAD) += head.o
15//kbuild:lib-$(CONFIG_HEAD) += head_tail.o
16
14//usage:#define head_trivial_usage 17//usage:#define head_trivial_usage
15//usage: "[OPTIONS] [FILE]..." 18//usage: "[OPTIONS] [FILE]..."
16//usage:#define head_full_usage "\n\n" 19//usage:#define head_full_usage "\n\n"
@@ -18,7 +21,8 @@
18//usage: "With more than one FILE, precede each with a filename header.\n" 21//usage: "With more than one FILE, precede each with a filename header.\n"
19//usage: "\n -n N[kbm] Print first N lines" 22//usage: "\n -n N[kbm] Print first N lines"
20//usage: IF_FEATURE_FANCY_HEAD( 23//usage: IF_FEATURE_FANCY_HEAD(
21//usage: "\n -c N[kbm] Print first N bytes" 24//usage: "\n -n -N[kbm] Print all except N last lines"
25//usage: "\n -c [-]N[kbm] Print first N bytes"
22//usage: "\n -q Never print headers" 26//usage: "\n -q Never print headers"
23//usage: "\n -v Always print headers" 27//usage: "\n -v Always print headers"
24//usage: ) 28//usage: )
@@ -31,9 +35,114 @@
31//usage: "daemon:x:1:1:daemon:/usr/sbin:/bin/sh\n" 35//usage: "daemon:x:1:1:daemon:/usr/sbin:/bin/sh\n"
32 36
33#include "libbb.h" 37#include "libbb.h"
38#include "head_tail.h"
34 39
35/* This is a NOEXEC applet. Be very careful! */ 40/* This is a NOEXEC applet. Be very careful! */
36 41
42#if !ENABLE_FEATURE_FANCY_HEAD
43# define print_first_N(fp,count,bytes) print_first_N(fp,count)
44#endif
45static void
46print_first_N(FILE *fp, unsigned long count, bool count_bytes)
47{
48#if !ENABLE_FEATURE_FANCY_HEAD
49 const int count_bytes = 0;
50#endif
51 while (count) {
52 int c = getc(fp);
53 if (c == EOF)
54 break;
55 if (count_bytes || (c == '\n'))
56 --count;
57 putchar(c);
58 }
59}
60
61#if ENABLE_FEATURE_FANCY_HEAD
62static void
63print_except_N_last_bytes(FILE *fp, unsigned count)
64{
65 unsigned char *circle = xmalloc(++count);
66 unsigned head = 0;
67 for(;;) {
68 int c;
69 c = getc(fp);
70 if (c == EOF)
71 goto ret;
72 circle[head++] = c;
73 if (head == count)
74 break;
75 }
76 for (;;) {
77 int c;
78 if (head == count)
79 head = 0;
80 putchar(circle[head]);
81 c = getc(fp);
82 if (c == EOF)
83 goto ret;
84 circle[head] = c;
85 head++;
86 }
87 ret:
88 free(circle);
89}
90
91static void
92print_except_N_last_lines(FILE *fp, unsigned count)
93{
94 char **circle = xzalloc((++count) * sizeof(circle[0]));
95 unsigned head = 0;
96 for(;;) {
97 char *c;
98 c = xmalloc_fgets(fp);
99 if (!c)
100 goto ret;
101 circle[head++] = c;
102 if (head == count)
103 break;
104 }
105 for (;;) {
106 char *c;
107 if (head == count)
108 head = 0;
109 fputs(circle[head], stdout);
110 c = xmalloc_fgets(fp);
111 if (!c)
112 goto ret;
113 free(circle[head]);
114 circle[head++] = c;
115 }
116 ret:
117 head = 0;
118 for(;;) {
119 free(circle[head++]);
120 if (head == count)
121 break;
122 }
123 free(circle);
124}
125#else
126/* Must never be called */
127void print_except_N_last_bytes(FILE *fp, unsigned count);
128void print_except_N_last_lines(FILE *fp, unsigned count);
129#endif
130
131#if !ENABLE_FEATURE_FANCY_HEAD
132# define eat_num(negative_N,p) eat_num(p)
133#endif
134static unsigned long
135eat_num(bool *negative_N, const char *p)
136{
137#if ENABLE_FEATURE_FANCY_HEAD
138 if (*p == '-') {
139 *negative_N = 1;
140 p++;
141 }
142#endif
143 return xatoul_sfx(p, head_tail_suffixes);
144}
145
37static const char head_opts[] ALIGN1 = 146static const char head_opts[] ALIGN1 =
38 "n:" 147 "n:"
39#if ENABLE_FEATURE_FANCY_HEAD 148#if ENABLE_FEATURE_FANCY_HEAD
@@ -41,29 +150,25 @@ static const char head_opts[] ALIGN1 =
41#endif 150#endif
42 ; 151 ;
43 152
44static const struct suffix_mult head_suffixes[] = {
45 { "b", 512 },
46 { "k", 1024 },
47 { "m", 1024*1024 },
48 { "", 0 }
49};
50
51#define header_fmt_str "\n==> %s <==\n" 153#define header_fmt_str "\n==> %s <==\n"
52 154
53int head_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 155int head_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
54int head_main(int argc, char **argv) 156int head_main(int argc, char **argv)
55{ 157{
56 unsigned long count = 10; 158 unsigned long count = 10;
57 unsigned long i;
58#if ENABLE_FEATURE_FANCY_HEAD 159#if ENABLE_FEATURE_FANCY_HEAD
59 int count_bytes = 0;
60 int header_threshhold = 1; 160 int header_threshhold = 1;
161 bool count_bytes = 0;
162 bool negative_N = 0;
163#else
164# define header_threshhold 1
165# define count_bytes 0
166# define negative_N 0
61#endif 167#endif
62 FILE *fp; 168 FILE *fp;
63 const char *fmt; 169 const char *fmt;
64 char *p; 170 char *p;
65 int opt; 171 int opt;
66 int c;
67 int retval = EXIT_SUCCESS; 172 int retval = EXIT_SUCCESS;
68 173
69#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD 174#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD
@@ -73,7 +178,7 @@ int head_main(int argc, char **argv)
73 ) { 178 ) {
74 --argc; 179 --argc;
75 ++argv; 180 ++argv;
76 p = (*argv) + 1; 181 p = argv[0] + 1;
77 goto GET_COUNT; 182 goto GET_COUNT;
78 } 183 }
79#endif 184#endif
@@ -97,7 +202,7 @@ int head_main(int argc, char **argv)
97#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD 202#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD
98 GET_COUNT: 203 GET_COUNT:
99#endif 204#endif
100 count = xatoul_sfx(p, head_suffixes); 205 count = eat_num(&negative_N, p);
101 break; 206 break;
102 default: 207 default:
103 bb_show_usage(); 208 bb_show_usage();
@@ -110,19 +215,17 @@ int head_main(int argc, char **argv)
110 *--argv = (char*)"-"; 215 *--argv = (char*)"-";
111 216
112 fmt = header_fmt_str + 1; 217 fmt = header_fmt_str + 1;
113#if ENABLE_FEATURE_FANCY_HEAD
114 if (argc <= header_threshhold) { 218 if (argc <= header_threshhold) {
219#if ENABLE_FEATURE_FANCY_HEAD
115 header_threshhold = 0; 220 header_threshhold = 0;
116 }
117#else 221#else
118 if (argc <= 1) {
119 fmt += 11; /* "" */ 222 fmt += 11; /* "" */
120 }
121 /* Now define some things here to avoid #ifdefs in the code below.
122 * These should optimize out of the if conditions below. */
123#define header_threshhold 1
124#define count_bytes 0
125#endif 223#endif
224 }
225 if (negative_N) {
226 if (count >= INT_MAX / sizeof(char*))
227 bb_error_msg("count is too big: %lu", count);
228 }
126 229
127 do { 230 do {
128 fp = fopen_or_warn_stdin(*argv); 231 fp = fopen_or_warn_stdin(*argv);
@@ -133,18 +236,20 @@ int head_main(int argc, char **argv)
133 if (header_threshhold) { 236 if (header_threshhold) {
134 printf(fmt, *argv); 237 printf(fmt, *argv);
135 } 238 }
136 i = count; 239 if (negative_N) {
137 while (i && ((c = getc(fp)) != EOF)) { 240 if (count_bytes) {
138 if (count_bytes || (c == '\n')) { 241 print_except_N_last_bytes(fp, count);
139 --i; 242 } else {
243 print_except_N_last_lines(fp, count);
140 } 244 }
141 putchar(c); 245 } else {
246 print_first_N(fp, count, count_bytes);
142 } 247 }
248 die_if_ferror_stdout();
143 if (fclose_if_not_stdin(fp)) { 249 if (fclose_if_not_stdin(fp)) {
144 bb_simple_perror_msg(*argv); 250 bb_simple_perror_msg(*argv);
145 retval = EXIT_FAILURE; 251 retval = EXIT_FAILURE;
146 } 252 }
147 die_if_ferror_stdout();
148 } else { 253 } else {
149 retval = EXIT_FAILURE; 254 retval = EXIT_FAILURE;
150 } 255 }
diff --git a/coreutils/head_tail.c b/coreutils/head_tail.c
new file mode 100644
index 000000000..1658c0d1b
--- /dev/null
+++ b/coreutils/head_tail.c
@@ -0,0 +1,14 @@
1/*
2 * Copyright (C) 2013 Denys Vlasenko
3 *
4 * Licensed under GPLv2, see file LICENSE in this source tree.
5 */
6#include "libbb.h"
7#include "head_tail.h"
8
9const struct suffix_mult head_tail_suffixes[] = {
10 { "b", 512 },
11 { "k", 1024 },
12 { "m", 1024*1024 },
13 { "", 0 }
14};
diff --git a/coreutils/head_tail.h b/coreutils/head_tail.h
new file mode 100644
index 000000000..df19e41e0
--- /dev/null
+++ b/coreutils/head_tail.h
@@ -0,0 +1,6 @@
1/*
2 * Copyright (C) 2013 Denys Vlasenko
3 *
4 * Licensed under GPLv2, see file LICENSE in this source tree.
5 */
6extern const struct suffix_mult head_tail_suffixes[];
diff --git a/coreutils/hostid.c b/coreutils/hostid.c
index 5c1a4e086..e5b1f5188 100644
--- a/coreutils/hostid.c
+++ b/coreutils/hostid.c
@@ -36,7 +36,8 @@ int hostid_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
36 bb_show_usage(); 36 bb_show_usage();
37 } 37 }
38 38
39 printf("%08lx\n", gethostid()); 39 /* POSIX says gethostid returns a "32-bit identifier" */
40 printf("%08x\n", (unsigned)(uint32_t)gethostid());
40 41
41 return fflush_all(); 42 return fflush_all();
42} 43}
diff --git a/coreutils/id.c b/coreutils/id.c
index 1f20b755e..1f3e1c4c2 100644
--- a/coreutils/id.c
+++ b/coreutils/id.c
@@ -64,12 +64,10 @@
64/* This is a NOEXEC applet. Be very careful! */ 64/* This is a NOEXEC applet. Be very careful! */
65 65
66#if !ENABLE_USE_BB_PWD_GRP 66#if !ENABLE_USE_BB_PWD_GRP
67#if defined(__UCLIBC_MAJOR__) && (__UCLIBC_MAJOR__ == 0) 67#if defined(__UCLIBC__) && UCLIBC_VERSION < KERNEL_VERSION(0, 9, 30)
68#if (__UCLIBC_MINOR__ < 9) || (__UCLIBC_MINOR__ == 9 && __UCLIBC_SUBLEVEL__ < 30)
69#error "Sorry, you need at least uClibc version 0.9.30 for id applet to build" 68#error "Sorry, you need at least uClibc version 0.9.30 for id applet to build"
70#endif 69#endif
71#endif 70#endif
72#endif
73 71
74enum { 72enum {
75 PRINT_REAL = (1 << 0), 73 PRINT_REAL = (1 << 0),
diff --git a/coreutils/readlink.c b/coreutils/readlink.c
index f7ad791ec..d73ef4ddb 100644
--- a/coreutils/readlink.c
+++ b/coreutils/readlink.c
@@ -39,7 +39,10 @@
39 * -q, --quiet, -s, --silent suppress most error messages 39 * -q, --quiet, -s, --silent suppress most error messages
40 * -v, --verbose report error messages 40 * -v, --verbose report error messages
41 * 41 *
42 * bbox supports: -f -n -v (fully), -q -s (accepts but ignores) 42 * bbox supports: -f (partially) -n -v (fully), -q -s (accepts but ignores)
43 * Note: we export the -f flag, but our -f behaves like coreutils' -e.
44 * Unfortunately, there isn't a C lib function we can leverage to get this
45 * behavior which means we'd have to implement the full stack ourselves :(.
43 */ 46 */
44 47
45int readlink_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 48int readlink_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
diff --git a/coreutils/tail.c b/coreutils/tail.c
index b376ec863..19fd8f695 100644
--- a/coreutils/tail.c
+++ b/coreutils/tail.c
@@ -24,6 +24,9 @@
24 * 7) lseek attempted when count==0 even if arg was +0 (from top) 24 * 7) lseek attempted when count==0 even if arg was +0 (from top)
25 */ 25 */
26 26
27//kbuild:lib-$(CONFIG_TAIL) += tail.o
28//kbuild:lib-$(CONFIG_TAIL) += head_tail.o
29
27//usage:#define tail_trivial_usage 30//usage:#define tail_trivial_usage
28//usage: "[OPTIONS] [FILE]..." 31//usage: "[OPTIONS] [FILE]..."
29//usage:#define tail_full_usage "\n\n" 32//usage:#define tail_full_usage "\n\n"
@@ -34,14 +37,13 @@
34//usage: "\n -s SECONDS Wait SECONDS between reads with -f" 37//usage: "\n -s SECONDS Wait SECONDS between reads with -f"
35//usage: ) 38//usage: )
36//usage: "\n -n N[kbm] Print last N lines" 39//usage: "\n -n N[kbm] Print last N lines"
40//usage: "\n -n +N[kbm] Start on Nth line and print the rest"
37//usage: IF_FEATURE_FANCY_TAIL( 41//usage: IF_FEATURE_FANCY_TAIL(
38//usage: "\n -c N[kbm] Print last N bytes" 42//usage: "\n -c [+]N[kbm] Print last N bytes"
39//usage: "\n -q Never print headers" 43//usage: "\n -q Never print headers"
40//usage: "\n -v Always print headers" 44//usage: "\n -v Always print headers"
41//usage: "\n" 45//usage: "\n"
42//usage: "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)." 46//usage: "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)."
43//usage: "\nIf N starts with a '+', output begins with the Nth item from the start"
44//usage: "\nof each file, not from the end."
45//usage: ) 47//usage: )
46//usage: 48//usage:
47//usage:#define tail_example_usage 49//usage:#define tail_example_usage
@@ -49,13 +51,7 @@
49//usage: "nameserver 10.0.0.1\n" 51//usage: "nameserver 10.0.0.1\n"
50 52
51#include "libbb.h" 53#include "libbb.h"
52 54#include "head_tail.h"
53static const struct suffix_mult tail_suffixes[] = {
54 { "b", 512 },
55 { "k", 1024 },
56 { "m", 1024*1024 },
57 { "", 0 }
58};
59 55
60struct globals { 56struct globals {
61 bool from_top; 57 bool from_top;
@@ -102,7 +98,7 @@ static unsigned eat_num(const char *p)
102 p++; 98 p++;
103 G.from_top = 1; 99 G.from_top = 1;
104 } 100 }
105 return xatou_sfx(p, tail_suffixes); 101 return xatou_sfx(p, head_tail_suffixes);
106} 102}
107 103
108int tail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 104int tail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
diff --git a/debianutils/run_parts.c b/debianutils/run_parts.c
index 005b30420..2c2b032be 100644
--- a/debianutils/run_parts.c
+++ b/debianutils/run_parts.c
@@ -21,24 +21,24 @@
21 * taken from debian-utils. I've only removed the long options and the 21 * taken from debian-utils. I've only removed the long options and the
22 * report mode. As the original run-parts support only long options, I've 22 * report mode. As the original run-parts support only long options, I've
23 * broken compatibility because the BusyBox policy doesn't allow them. 23 * broken compatibility because the BusyBox policy doesn't allow them.
24 * The supported options are:
25 * -t test. Print the name of the files to be executed, without
26 * execute them.
27 * -a ARG argument. Pass ARG as an argument the program executed. It can
28 * be repeated to pass multiple arguments.
29 * -u MASK umask. Set the umask of the program executed to MASK.
30 */ 24 */
31 25
32//usage:#define run_parts_trivial_usage 26//usage:#define run_parts_trivial_usage
33//usage: "[-t"IF_FEATURE_RUN_PARTS_FANCY("l")"] [-a ARG]... [-u MASK] DIRECTORY" 27//usage: "[-a ARG]... [-u UMASK] "
28//usage: IF_FEATURE_RUN_PARTS_LONG_OPTIONS("[--reverse] [--test] [--exit-on-error] "IF_FEATURE_RUN_PARTS_FANCY("[--list] "))
29//usage: "DIRECTORY"
34//usage:#define run_parts_full_usage "\n\n" 30//usage:#define run_parts_full_usage "\n\n"
35//usage: "Run a bunch of scripts in DIRECTORY\n" 31//usage: "Run a bunch of scripts in DIRECTORY\n"
36//usage: "\n -t Dry run" 32//usage: "\n -a ARG Pass ARG as argument to scripts"
33//usage: "\n -u UMASK Set UMASK before running scripts"
34//usage: IF_FEATURE_RUN_PARTS_LONG_OPTIONS(
35//usage: "\n --reverse Reverse execution order"
36//usage: "\n --test Dry run"
37//usage: "\n --exit-on-error Exit if a script exits with non-zero"
37//usage: IF_FEATURE_RUN_PARTS_FANCY( 38//usage: IF_FEATURE_RUN_PARTS_FANCY(
38//usage: "\n -l Print names of matching files even if they are not executable" 39//usage: "\n --list Print names of matching files even if they are not executable"
40//usage: )
39//usage: ) 41//usage: )
40//usage: "\n -a ARG Pass ARG as argument to programs"
41//usage: "\n -u MASK Set umask to MASK before running programs"
42//usage: 42//usage:
43//usage:#define run_parts_example_usage 43//usage:#define run_parts_example_usage
44//usage: "$ run-parts -a start /etc/init.d\n" 44//usage: "$ run-parts -a start /etc/init.d\n"
@@ -70,19 +70,15 @@ struct globals {
70enum { NUM_CMD = (COMMON_BUFSIZE - sizeof(G)) / sizeof(cmd[0]) - 1 }; 70enum { NUM_CMD = (COMMON_BUFSIZE - sizeof(G)) / sizeof(cmd[0]) - 1 };
71 71
72enum { 72enum {
73 OPT_r = (1 << 0), 73 OPT_a = (1 << 0),
74 OPT_a = (1 << 1), 74 OPT_u = (1 << 1),
75 OPT_u = (1 << 2), 75 OPT_r = (1 << 2) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS,
76 OPT_t = (1 << 3), 76 OPT_t = (1 << 3) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS,
77 OPT_l = (1 << 4) * ENABLE_FEATURE_RUN_PARTS_FANCY, 77 OPT_e = (1 << 4) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS,
78 OPT_l = (1 << 5) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS
79 * ENABLE_FEATURE_RUN_PARTS_FANCY,
78}; 80};
79 81
80#if ENABLE_FEATURE_RUN_PARTS_FANCY
81#define list_mode (option_mask32 & OPT_l)
82#else
83#define list_mode 0
84#endif
85
86/* Is this a valid filename (upper/lower alpha, digits, 82/* Is this a valid filename (upper/lower alpha, digits,
87 * underscores, and hyphens only?) 83 * underscores, and hyphens only?)
88 */ 84 */
@@ -110,7 +106,7 @@ static int FAST_FUNC act(const char *file, struct stat *statbuf, void *args UNUS
110 if (depth == 2 106 if (depth == 2
111 && ( !(statbuf->st_mode & (S_IFREG | S_IFLNK)) 107 && ( !(statbuf->st_mode & (S_IFREG | S_IFLNK))
112 || invalid_name(file) 108 || invalid_name(file)
113 || (!list_mode && access(file, X_OK) != 0)) 109 || (!(option_mask32 & OPT_l) && access(file, X_OK) != 0))
114 ) { 110 ) {
115 return SKIP; 111 return SKIP;
116 } 112 }
@@ -126,11 +122,12 @@ static int FAST_FUNC act(const char *file, struct stat *statbuf, void *args UNUS
126static const char runparts_longopts[] ALIGN1 = 122static const char runparts_longopts[] ALIGN1 =
127 "arg\0" Required_argument "a" 123 "arg\0" Required_argument "a"
128 "umask\0" Required_argument "u" 124 "umask\0" Required_argument "u"
129 "test\0" No_argument "t"
130#if ENABLE_FEATURE_RUN_PARTS_FANCY
131 "list\0" No_argument "l"
132 "reverse\0" No_argument "r"
133//TODO: "verbose\0" No_argument "v" 125//TODO: "verbose\0" No_argument "v"
126 "reverse\0" No_argument "\xf0"
127 "test\0" No_argument "\xf1"
128 "exit-on-error\0" No_argument "\xf2"
129#if ENABLE_FEATURE_RUN_PARTS_FANCY
130 "list\0" No_argument "\xf3"
134#endif 131#endif
135 ; 132 ;
136#endif 133#endif
@@ -150,7 +147,7 @@ int run_parts_main(int argc UNUSED_PARAM, char **argv)
150#endif 147#endif
151 /* We require exactly one argument: the directory name */ 148 /* We require exactly one argument: the directory name */
152 opt_complementary = "=1:a::"; 149 opt_complementary = "=1:a::";
153 getopt32(argv, "ra:u:t"IF_FEATURE_RUN_PARTS_FANCY("l"), &arg_list, &umask_p); 150 getopt32(argv, "a:u:", &arg_list, &umask_p);
154 151
155 umask(xstrtou_range(umask_p, 8, 0, 07777)); 152 umask(xstrtou_range(umask_p, 8, 0, 07777));
156 153
@@ -193,6 +190,9 @@ int run_parts_main(int argc UNUSED_PARAM, char **argv)
193 bb_perror_msg("can't execute '%s'", name); 190 bb_perror_msg("can't execute '%s'", name);
194 else /* ret > 0 */ 191 else /* ret > 0 */
195 bb_error_msg("%s exited with code %d", name, ret & 0xff); 192 bb_error_msg("%s exited with code %d", name, ret & 0xff);
193
194 if (option_mask32 & OPT_e)
195 xfunc_die();
196 } 196 }
197 197
198 return n; 198 return n;
diff --git a/docs/mdev.txt b/docs/mdev.txt
index 61f93c9df..b24025f7b 100644
--- a/docs/mdev.txt
+++ b/docs/mdev.txt
@@ -51,9 +51,9 @@ device nodes if your system needs something more than the default root/root
51660 permissions. 51660 permissions.
52 52
53The file has the format: 53The file has the format:
54 [-]<device regex> <uid>:<gid> <permissions> 54 [-][envmatch]<device regex> <uid>:<gid> <permissions>
55or 55or
56 @<maj[,min1[-min2]]> <uid>:<gid> <permissions> 56 [envmatch]@<maj[,min1[-min2]]> <uid>:<gid> <permissions>
57or 57or
58 $envvar=<regex> <uid>:<gid> <permissions> 58 $envvar=<regex> <uid>:<gid> <permissions>
59 59
diff --git a/editors/awk.c b/editors/awk.c
index 3224788c0..0b573a065 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -2661,7 +2661,8 @@ static var *evaluate(node *op, var *res)
2661 var *vbeg, *v; 2661 var *vbeg, *v;
2662 const char *sv_progname; 2662 const char *sv_progname;
2663 2663
2664 if (!op->r.f->body.first) 2664 /* The body might be empty, still has to eval the args */
2665 if (!op->r.n->info)
2665 syntax_error(EMSG_UNDEF_FUNC); 2666 syntax_error(EMSG_UNDEF_FUNC);
2666 2667
2667 vbeg = v = nvalloc(op->r.f->nargs + 1); 2668 vbeg = v = nvalloc(op->r.f->nargs + 1);
diff --git a/editors/vi.c b/editors/vi.c
index 1fc66b931..63b984ea6 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -478,6 +478,7 @@ static void flash(int); // flash the terminal screen
478static void show_status_line(void); // put a message on the bottom line 478static void show_status_line(void); // put a message on the bottom line
479static void status_line(const char *, ...); // print to status buf 479static void status_line(const char *, ...); // print to status buf
480static void status_line_bold(const char *, ...); 480static void status_line_bold(const char *, ...);
481static void status_line_bold_errno(const char *fn);
481static void not_implemented(const char *); // display "Not implemented" message 482static void not_implemented(const char *); // display "Not implemented" message
482static int format_edit_status(void); // format file status on status line 483static int format_edit_status(void); // format file status on status line
483static void redraw(int); // force a full screen refresh 484static void redraw(int); // force a full screen refresh
@@ -1321,7 +1322,7 @@ static void colon(char *buf)
1321 } 1322 }
1322 if (l < 0) { 1323 if (l < 0) {
1323 if (l == -1) 1324 if (l == -1)
1324 status_line_bold("'%s' %s", fn, strerror(errno)); 1325 status_line_bold_errno(fn);
1325 } else { 1326 } else {
1326 status_line("'%s' %dL, %dC", fn, li, l); 1327 status_line("'%s' %dL, %dC", fn, li, l);
1327 if (q == text && r == end - 1 && l == ch) { 1328 if (q == text && r == end - 1 && l == ch) {
@@ -2512,7 +2513,7 @@ static int file_insert(const char *fn, char *p, int update_ro_status)
2512 2513
2513 /* Validate file */ 2514 /* Validate file */
2514 if (stat(fn, &statbuf) < 0) { 2515 if (stat(fn, &statbuf) < 0) {
2515 status_line_bold("'%s' %s", fn, strerror(errno)); 2516 status_line_bold_errno(fn);
2516 goto fi0; 2517 goto fi0;
2517 } 2518 }
2518 if (!S_ISREG(statbuf.st_mode)) { 2519 if (!S_ISREG(statbuf.st_mode)) {
@@ -2528,18 +2529,18 @@ static int file_insert(const char *fn, char *p, int update_ro_status)
2528 // read file to buffer 2529 // read file to buffer
2529 fd = open(fn, O_RDONLY); 2530 fd = open(fn, O_RDONLY);
2530 if (fd < 0) { 2531 if (fd < 0) {
2531 status_line_bold("'%s' %s", fn, strerror(errno)); 2532 status_line_bold_errno(fn);
2532 goto fi0; 2533 goto fi0;
2533 } 2534 }
2534 size = (statbuf.st_size < INT_MAX ? (int)statbuf.st_size : INT_MAX); 2535 size = (statbuf.st_size < INT_MAX ? (int)statbuf.st_size : INT_MAX);
2535 p += text_hole_make(p, size); 2536 p += text_hole_make(p, size);
2536 cnt = safe_read(fd, p, size); 2537 cnt = safe_read(fd, p, size);
2537 if (cnt < 0) { 2538 if (cnt < 0) {
2538 status_line_bold("'%s' %s", fn, strerror(errno)); 2539 status_line_bold_errno(fn);
2539 p = text_hole_delete(p, p + size - 1); // un-do buffer insert 2540 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
2540 } else if (cnt < size) { 2541 } else if (cnt < size) {
2541 // There was a partial read, shrink unused space text[] 2542 // There was a partial read, shrink unused space text[]
2542 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert 2543 p = text_hole_delete(p + cnt, p + size - 1); // un-do buffer insert
2543 status_line_bold("can't read '%s'", fn); 2544 status_line_bold("can't read '%s'", fn);
2544 } 2545 }
2545 if (cnt >= size) 2546 if (cnt >= size)
@@ -2726,6 +2727,11 @@ static void status_line_bold(const char *format, ...)
2726 have_status_msg = 1 + sizeof(ESC_BOLD_TEXT) + sizeof(ESC_NORM_TEXT) - 2; 2727 have_status_msg = 1 + sizeof(ESC_BOLD_TEXT) + sizeof(ESC_NORM_TEXT) - 2;
2727} 2728}
2728 2729
2730static void status_line_bold_errno(const char *fn)
2731{
2732 status_line_bold("'%s' %s", fn, strerror(errno));
2733}
2734
2729// format status buffer 2735// format status buffer
2730static void status_line(const char *format, ...) 2736static void status_line(const char *format, ...)
2731{ 2737{
diff --git a/examples/mdev_fat.conf b/examples/mdev_fat.conf
index ceba3a797..bceddb2d7 100644
--- a/examples/mdev_fat.conf
+++ b/examples/mdev_fat.conf
@@ -7,9 +7,9 @@
7# instead of the default 0:0 660. 7# instead of the default 0:0 660.
8# 8#
9# Syntax: 9# Syntax:
10# [-]devicename_regex user:group mode [=path]|[>path]|[!] [@|$|*cmd args...] 10# [-][ENVVAR=regex;]...devicename_regex user:group mode [=path]|[>path]|[!] [@|$|*cmd args...]
11# [-]$ENVVAR=regex user:group mode [=path]|[>path]|[!] [@|$|*cmd args...] 11# [-][ENVVAR=regex;]...@maj,min[-min2] user:group mode [=path]|[>path]|[!] [@|$|*cmd args...]
12# [-]@maj,min[-min2] user:group mode [=path]|[>path]|[!] [@|$|*cmd args...] 12# [-]$ENVVAR=regex user:group mode [=path]|[>path]|[!] [@|$|*cmd args...]
13# 13#
14# [-]: do not stop on this match, continue reading mdev.conf 14# [-]: do not stop on this match, continue reading mdev.conf
15# =: move, >: move and create a symlink 15# =: move, >: move and create a symlink
@@ -53,6 +53,7 @@ sr[0-9]* root:cdrom 660 @ln -sf $MDEV cdrom
53fd[0-9]* root:floppy 660 53fd[0-9]* root:floppy 660
54 54
55# net devices 55# net devices
56SUBSYSTEM=net;.* root:root 600 @nameif
56tun[0-9]* root:root 600 =net/ 57tun[0-9]* root:root 600 =net/
57tap[0-9]* root:root 600 =net/ 58tap[0-9]* root:root 600 =net/
58 59
diff --git a/examples/udhcp/simple.script b/examples/udhcp/simple.script
index 40ee73822..2a917eb6c 100755
--- a/examples/udhcp/simple.script
+++ b/examples/udhcp/simple.script
@@ -29,18 +29,23 @@ case "$1" in
29 metric=0 29 metric=0
30 for i in $router ; do 30 for i in $router ; do
31 echo "Adding router $i" 31 echo "Adding router $i"
32 route add default gw $i dev $interface metric $((metric++)) 32 route add default gw $i dev $interface metric $metric
33 : $(( metric += 1 ))
33 done 34 done
34 fi 35 fi
35 36
36 echo "Recreating $RESOLV_CONF" 37 echo "Recreating $RESOLV_CONF"
37 echo -n > $RESOLV_CONF-$$ 38 # If the file is a symlink somewhere (like /etc/resolv.conf
38 [ -n "$domain" ] && echo "search $domain" >> $RESOLV_CONF-$$ 39 # pointing to /run/resolv.conf), make sure things work.
40 realconf=$(readlink -f "$RESOLV_CONF" 2>/dev/null || echo "$RESOLV_CONF")
41 tmpfile="$realconf-$$"
42 > "$tmpfile"
43 [ -n "$domain" ] && echo "search $domain" >> "$tmpfile"
39 for i in $dns ; do 44 for i in $dns ; do
40 echo " Adding DNS server $i" 45 echo " Adding DNS server $i"
41 echo "nameserver $i" >> $RESOLV_CONF-$$ 46 echo "nameserver $i" >> "$tmpfile"
42 done 47 done
43 mv $RESOLV_CONF-$$ $RESOLV_CONF 48 mv "$tmpfile" "$realconf"
44 ;; 49 ;;
45esac 50esac
46 51
diff --git a/include/bb_archive.h b/include/bb_archive.h
index a7a2a1135..b82cfd83c 100644
--- a/include/bb_archive.h
+++ b/include/bb_archive.h
@@ -122,6 +122,9 @@ typedef struct archive_handle_t {
122#define ARCHIVE_NUMERIC_OWNER (1 << 7) 122#define ARCHIVE_NUMERIC_OWNER (1 << 7)
123#define ARCHIVE_O_TRUNC (1 << 8) 123#define ARCHIVE_O_TRUNC (1 << 8)
124#define ARCHIVE_REMEMBER_NAMES (1 << 9) 124#define ARCHIVE_REMEMBER_NAMES (1 << 9)
125#if ENABLE_RPM
126#define ARCHIVE_REPLACE_VIA_RENAME (1 << 10)
127#endif
125 128
126 129
127/* POSIX tar Header Block, from POSIX 1003.1-1990 */ 130/* POSIX tar Header Block, from POSIX 1003.1-1990 */
diff --git a/include/libbb.h b/include/libbb.h
index c5ff51398..15a5a6623 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -399,6 +399,7 @@ char *bb_get_last_path_component_nostrip(const char *path) FAST_FUNC;
399const char *bb_basename(const char *name) FAST_FUNC; 399const char *bb_basename(const char *name) FAST_FUNC;
400/* NB: can violate const-ness (similarly to strchr) */ 400/* NB: can violate const-ness (similarly to strchr) */
401char *last_char_is(const char *s, int c) FAST_FUNC; 401char *last_char_is(const char *s, int c) FAST_FUNC;
402const char* endofname(const char *name) FAST_FUNC;
402 403
403void ndelay_on(int fd) FAST_FUNC; 404void ndelay_on(int fd) FAST_FUNC;
404void ndelay_off(int fd) FAST_FUNC; 405void ndelay_off(int fd) FAST_FUNC;
diff --git a/include/platform.h b/include/platform.h
index b3eee55ee..fa4267b4b 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -245,7 +245,7 @@ typedef uint32_t bb__aliased_uint32_t FIX_ALIASING;
245# define move_from_unaligned32(v, u32p) (memcpy(&(v), (u32p), 4)) 245# define move_from_unaligned32(v, u32p) (memcpy(&(v), (u32p), 4))
246# define move_to_unaligned16(u16p, v) do { \ 246# define move_to_unaligned16(u16p, v) do { \
247 uint16_t __t = (v); \ 247 uint16_t __t = (v); \
248 memcpy((u16p), &__t, 4); \ 248 memcpy((u16p), &__t, 2); \
249} while (0) 249} while (0)
250# define move_to_unaligned32(u32p, v) do { \ 250# define move_to_unaligned32(u32p, v) do { \
251 uint32_t __t = (v); \ 251 uint32_t __t = (v); \
@@ -280,6 +280,12 @@ typedef unsigned smalluint;
280 280
281#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) 281#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
282 282
283#ifdef __UCLIBC__
284# define UCLIBC_VERSION KERNEL_VERSION(__UCLIBC_MAJOR__, __UCLIBC_MINOR__, __UCLIBC_SUBLEVEL__)
285#else
286# define UCLIBC_VERSION 0
287#endif
288
283 289
284/* ---- Miscellaneous --------------------------------------- */ 290/* ---- Miscellaneous --------------------------------------- */
285 291
@@ -322,8 +328,9 @@ typedef unsigned smalluint;
322 * for a mmu-less system. 328 * for a mmu-less system.
323 */ 329 */
324#if ENABLE_NOMMU || \ 330#if ENABLE_NOMMU || \
325 (defined __UCLIBC__ && __UCLIBC_MAJOR__ >= 0 && __UCLIBC_MINOR__ >= 9 && \ 331 (defined __UCLIBC__ && \
326 __UCLIBC_SUBLEVEL__ > 28 && !defined __ARCH_USE_MMU__) 332 UCLIBC_VERSION > KERNEL_VERSION(0, 9, 28) && \
333 !defined __ARCH_USE_MMU__)
327# define BB_MMU 0 334# define BB_MMU 0
328# define USE_FOR_NOMMU(...) __VA_ARGS__ 335# define USE_FOR_NOMMU(...) __VA_ARGS__
329# define USE_FOR_MMU(...) 336# define USE_FOR_MMU(...)
@@ -390,13 +397,8 @@ typedef unsigned smalluint;
390#define HAVE_NET_ETHERNET_H 1 397#define HAVE_NET_ETHERNET_H 1
391#define HAVE_SYS_STATFS_H 1 398#define HAVE_SYS_STATFS_H 1
392 399
393#if defined(__UCLIBC_MAJOR__) 400#if defined(__UCLIBC__) && UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32)
394# if __UCLIBC_MAJOR__ == 0 \ 401# undef HAVE_STRVERSCMP
395 && ( __UCLIBC_MINOR__ < 9 \
396 || (__UCLIBC_MINOR__ == 9 && __UCLIBC_SUBLEVEL__ < 32) \
397 )
398# undef HAVE_STRVERSCMP
399# endif
400#endif 402#endif
401 403
402#if defined(__dietlibc__) 404#if defined(__dietlibc__)
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index fb06ab9a5..f0df4e080 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -752,7 +752,7 @@ void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv)
752//TODO: just compare applet_no with APPLET_NO_test 752//TODO: just compare applet_no with APPLET_NO_test
753 if (!ENABLE_TEST || strcmp(applet_name, "test") != 0) { 753 if (!ENABLE_TEST || strcmp(applet_name, "test") != 0) {
754 /* If you want "foo --help" to return 0: */ 754 /* If you want "foo --help" to return 0: */
755 /*xfunc_error_retval = 0;*/ 755 xfunc_error_retval = 0;
756 bb_show_usage(); 756 bb_show_usage();
757 } 757 }
758 } 758 }
diff --git a/libbb/endofname.c b/libbb/endofname.c
new file mode 100644
index 000000000..305f0932b
--- /dev/null
+++ b/libbb/endofname.c
@@ -0,0 +1,26 @@
1/*
2 * Utility routines.
3 *
4 * Copyright (C) 2013 Denys Vlasenko
5 *
6 * Licensed under GPLv2, see file LICENSE in this source tree.
7 */
8
9//kbuild:lib-y += endofname.o
10
11#include "libbb.h"
12
13#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
14#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
15
16const char* FAST_FUNC
17endofname(const char *name)
18{
19 if (!is_name(*name))
20 return name;
21 while (*++name) {
22 if (!is_in_name(*name))
23 break;
24 }
25 return name;
26}
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index 8a276d941..09e203159 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -187,6 +187,7 @@ extern struct lineedit_statics *const lineedit_ptr_to_statics;
187 cmdedit_termw = 80; \ 187 cmdedit_termw = 80; \
188 IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines = 1;) \ 188 IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines = 1;) \
189 IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) \ 189 IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) \
190 IF_FEATURE_EDITING_VI(delptr = delbuf;) \
190} while (0) 191} while (0)
191 192
192static void deinit_S(void) 193static void deinit_S(void)
diff --git a/libbb/platform.c b/libbb/platform.c
index 2bf34f5bc..19734517b 100644
--- a/libbb/platform.c
+++ b/libbb/platform.c
@@ -28,14 +28,16 @@ int FAST_FUNC vasprintf(char **string_ptr, const char *format, va_list p)
28 r = vsnprintf(buf, 128, format, p); 28 r = vsnprintf(buf, 128, format, p);
29 va_end(p); 29 va_end(p);
30 30
31 /* Note: can't use xstrdup/xmalloc, they call vasprintf (us) on failure! */
32
31 if (r < 128) { 33 if (r < 128) {
32 va_end(p2); 34 va_end(p2);
33 *string_ptr = xstrdup(buf); 35 *string_ptr = strdup(buf);
34 return r; 36 return (*string_ptr ? r : -1);
35 } 37 }
36 38
37 *string_ptr = xmalloc(r+1); 39 *string_ptr = malloc(r+1);
38 r = vsnprintf(*string_ptr, r+1, format, p2); 40 r = (*string_ptr ? vsnprintf(*string_ptr, r+1, format, p2) : -1);
39 va_end(p2); 41 va_end(p2);
40 42
41 return r; 43 return r;
diff --git a/libbb/xreadlink.c b/libbb/xreadlink.c
index f2faa6d7a..bc1d0e665 100644
--- a/libbb/xreadlink.c
+++ b/libbb/xreadlink.c
@@ -102,7 +102,8 @@ char* FAST_FUNC xmalloc_readlink_or_warn(const char *path)
102 102
103char* FAST_FUNC xmalloc_realpath(const char *path) 103char* FAST_FUNC xmalloc_realpath(const char *path)
104{ 104{
105#if defined(__GLIBC__) && !defined(__UCLIBC__) 105#if defined(__GLIBC__) || \
106 (defined(__UCLIBC__) && UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 31))
106 /* glibc provides a non-standard extension */ 107 /* glibc provides a non-standard extension */
107 /* new: POSIX.1-2008 specifies this behavior as well */ 108 /* new: POSIX.1-2008 specifies this behavior as well */
108 return realpath(path, NULL); 109 return realpath(path, NULL);
diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c
index c426e9d85..b5aa1d17b 100644
--- a/mailutils/sendmail.c
+++ b/mailutils/sendmail.c
@@ -92,25 +92,73 @@ static int smtp_check(const char *fmt, int code)
92// strip argument of bad chars 92// strip argument of bad chars
93static char *sane_address(char *str) 93static char *sane_address(char *str)
94{ 94{
95 char *s = str; 95 char *s;
96 char *p = s; 96
97 trim(str);
98 s = str;
97 while (*s) { 99 while (*s) {
98 if (isalnum(*s) || '_' == *s || '-' == *s || '.' == *s || '@' == *s) { 100 if (!isalnum(*s) && !strchr("_-.@", *s)) {
99 *p++ = *s; 101 bb_error_msg("bad address '%s'", str);
102 /* returning "": */
103 str[0] = '\0';
104 return str;
100 } 105 }
101 s++; 106 s++;
102 } 107 }
103 *p = '\0';
104 return str; 108 return str;
105} 109}
106 110
111// check for an address inside angle brackets, if not found fall back to normal
112static char *angle_address(char *str)
113{
114 char *s, *e;
115
116 trim(str);
117 e = last_char_is(str, '>');
118 if (e) {
119 s = strrchr(str, '<');
120 if (s) {
121 *e = '\0';
122 str = s + 1;
123 }
124 }
125 return sane_address(str);
126}
127
107static void rcptto(const char *s) 128static void rcptto(const char *s)
108{ 129{
130 if (!*s)
131 return;
109 // N.B. we don't die if recipient is rejected, for the other recipients may be accepted 132 // N.B. we don't die if recipient is rejected, for the other recipients may be accepted
110 if (250 != smtp_checkp("RCPT TO:<%s>", s, -1)) 133 if (250 != smtp_checkp("RCPT TO:<%s>", s, -1))
111 bb_error_msg("Bad recipient: <%s>", s); 134 bb_error_msg("Bad recipient: <%s>", s);
112} 135}
113 136
137// send to a list of comma separated addresses
138static void rcptto_list(const char *list)
139{
140 char *str = xstrdup(list);
141 char *s = str;
142 char prev = 0;
143 int in_quote = 0;
144
145 while (*s) {
146 char ch = *s++;
147
148 if (ch == '"' && prev != '\\') {
149 in_quote = !in_quote;
150 } else if (!in_quote && ch == ',') {
151 s[-1] = '\0';
152 rcptto(angle_address(str));
153 str = s;
154 }
155 prev = ch;
156 }
157 if (prev != ',')
158 rcptto(angle_address(str));
159 free(str);
160}
161
114int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 162int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
115int sendmail_main(int argc UNUSED_PARAM, char **argv) 163int sendmail_main(int argc UNUSED_PARAM, char **argv)
116{ 164{
@@ -121,6 +169,13 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
121 char *host = sane_address(safe_gethostname()); 169 char *host = sane_address(safe_gethostname());
122 unsigned nheaders = 0; 170 unsigned nheaders = 0;
123 int code; 171 int code;
172 enum {
173 HDR_OTHER = 0,
174 HDR_TOCC,
175 HDR_BCC,
176 } last_hdr = 0;
177 int check_hdr;
178 int has_to = 0;
124 179
125 enum { 180 enum {
126 //--- standard options 181 //--- standard options
@@ -282,23 +337,36 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
282 337
283 // analyze headers 338 // analyze headers
284 // To: or Cc: headers add recipients 339 // To: or Cc: headers add recipients
340 check_hdr = (0 == strncasecmp("To:", s, 3));
341 has_to |= check_hdr;
285 if (opts & OPT_t) { 342 if (opts & OPT_t) {
286 if (0 == strncasecmp("To:", s, 3) || 0 == strncasecmp("Bcc:" + 1, s, 3)) { 343 if (check_hdr || 0 == strncasecmp("Bcc:" + 1, s, 3)) {
287 rcptto(sane_address(s+3)); 344 rcptto_list(s+3);
345 last_hdr = HDR_TOCC;
288 goto addheader; 346 goto addheader;
289 } 347 }
290 // Bcc: header adds blind copy (hidden) recipient 348 // Bcc: header adds blind copy (hidden) recipient
291 if (0 == strncasecmp("Bcc:", s, 4)) { 349 if (0 == strncasecmp("Bcc:", s, 4)) {
292 rcptto(sane_address(s+4)); 350 rcptto_list(s+4);
293 free(s); 351 free(s);
352 last_hdr = HDR_BCC;
294 continue; // N.B. Bcc: vanishes from headers! 353 continue; // N.B. Bcc: vanishes from headers!
295 } 354 }
296 } 355 }
297 if (strchr(s, ':') || (list && isspace(s[0]))) { 356 check_hdr = (list && isspace(s[0]));
357 if (strchr(s, ':') || check_hdr) {
298 // other headers go verbatim 358 // other headers go verbatim
299 // N.B. RFC2822 2.2.3 "Long Header Fields" allows for headers to occupy several lines. 359 // N.B. RFC2822 2.2.3 "Long Header Fields" allows for headers to occupy several lines.
300 // Continuation is denoted by prefixing additional lines with whitespace(s). 360 // Continuation is denoted by prefixing additional lines with whitespace(s).
301 // Thanks (stefan.seyfried at googlemail.com) for pointing this out. 361 // Thanks (stefan.seyfried at googlemail.com) for pointing this out.
362 if (check_hdr && last_hdr != HDR_OTHER) {
363 rcptto_list(s+1);
364 if (last_hdr == HDR_BCC)
365 continue;
366 // N.B. Bcc: vanishes from headers!
367 } else {
368 last_hdr = HDR_OTHER;
369 }
302 addheader: 370 addheader:
303 // N.B. we allow MAX_HEADERS generic headers at most to prevent attacks 371 // N.B. we allow MAX_HEADERS generic headers at most to prevent attacks
304 if (MAX_HEADERS && ++nheaders >= MAX_HEADERS) 372 if (MAX_HEADERS && ++nheaders >= MAX_HEADERS)
@@ -309,12 +377,27 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
309 // so stop "analyze headers" mode 377 // so stop "analyze headers" mode
310 reenter: 378 reenter:
311 // put recipients specified on cmdline 379 // put recipients specified on cmdline
380 check_hdr = 1;
312 while (*argv) { 381 while (*argv) {
313 char *t = sane_address(*argv); 382 char *t = sane_address(*argv);
314 rcptto(t); 383 rcptto(t);
315 //if (MAX_HEADERS && ++nheaders >= MAX_HEADERS) 384 //if (MAX_HEADERS && ++nheaders >= MAX_HEADERS)
316 // goto bail; 385 // goto bail;
317 llist_add_to_end(&list, xasprintf("To: %s", t)); 386 if (!has_to) {
387 const char *hdr;
388
389 if (check_hdr && argv[1])
390 hdr = "To: %s,";
391 else if (check_hdr)
392 hdr = "To: %s";
393 else if (argv[1])
394 hdr = "To: %s," + 3;
395 else
396 hdr = "To: %s" + 3;
397 llist_add_to_end(&list,
398 xasprintf(hdr, t));
399 check_hdr = 0;
400 }
318 argv++; 401 argv++;
319 } 402 }
320 // enter "put message" mode 403 // enter "put message" mode
diff --git a/miscutils/flash_eraseall.c b/miscutils/flash_eraseall.c
index 0598371d5..bf9b739a1 100644
--- a/miscutils/flash_eraseall.c
+++ b/miscutils/flash_eraseall.c
@@ -11,10 +11,11 @@
11 */ 11 */
12 12
13//usage:#define flash_eraseall_trivial_usage 13//usage:#define flash_eraseall_trivial_usage
14//usage: "[-jq] MTD_DEVICE" 14//usage: "[-jNq] MTD_DEVICE"
15//usage:#define flash_eraseall_full_usage "\n\n" 15//usage:#define flash_eraseall_full_usage "\n\n"
16//usage: "Erase an MTD device\n" 16//usage: "Erase an MTD device\n"
17//usage: "\n -j Format the device for jffs2" 17//usage: "\n -j Format the device for jffs2"
18//usage: "\n -N Don't skip bad blocks"
18//usage: "\n -q Don't display progress messages" 19//usage: "\n -q Don't display progress messages"
19 20
20#include "libbb.h" 21#include "libbb.h"
@@ -22,9 +23,9 @@
22#include <linux/jffs2.h> 23#include <linux/jffs2.h>
23 24
24#define OPTION_J (1 << 0) 25#define OPTION_J (1 << 0)
25#define OPTION_Q (1 << 1) 26#define OPTION_N (1 << 1)
26#define IS_NAND (1 << 2) 27#define OPTION_Q (1 << 2)
27#define BBTEST (1 << 3) 28#define IS_NAND (1 << 3)
28 29
29/* mtd/jffs2-user.h used to have this atrocity: 30/* mtd/jffs2-user.h used to have this atrocity:
30extern int target_endian; 31extern int target_endian;
@@ -71,7 +72,7 @@ int flash_eraseall_main(int argc UNUSED_PARAM, char **argv)
71 char *mtd_name; 72 char *mtd_name;
72 73
73 opt_complementary = "=1"; 74 opt_complementary = "=1";
74 flags = BBTEST | getopt32(argv, "jq"); 75 flags = getopt32(argv, "jNq");
75 76
76 mtd_name = argv[optind]; 77 mtd_name = argv[optind];
77 fd = xopen(mtd_name, O_RDWR); 78 fd = xopen(mtd_name, O_RDWR);
@@ -139,7 +140,7 @@ int flash_eraseall_main(int argc UNUSED_PARAM, char **argv)
139 140
140 for (erase.start = 0; erase.start < meminfo.size; 141 for (erase.start = 0; erase.start < meminfo.size;
141 erase.start += meminfo.erasesize) { 142 erase.start += meminfo.erasesize) {
142 if (flags & BBTEST) { 143 if (!(flags & OPTION_N)) {
143 int ret; 144 int ret;
144 loff_t offset = erase.start; 145 loff_t offset = erase.start;
145 146
@@ -154,7 +155,7 @@ int flash_eraseall_main(int argc UNUSED_PARAM, char **argv)
154 * types e.g. NOR 155 * types e.g. NOR
155 */ 156 */
156 if (errno == EOPNOTSUPP) { 157 if (errno == EOPNOTSUPP) {
157 flags &= ~BBTEST; 158 flags |= OPTION_N;
158 if (flags & IS_NAND) 159 if (flags & IS_NAND)
159 bb_error_msg_and_die("bad block check not available"); 160 bb_error_msg_and_die("bad block check not available");
160 } else { 161 } else {
diff --git a/miscutils/nandwrite.c b/miscutils/nandwrite.c
index 5908ac773..e3f9b565d 100644
--- a/miscutils/nandwrite.c
+++ b/miscutils/nandwrite.c
@@ -23,7 +23,7 @@
23//config: Dump the content of raw NAND chip 23//config: Dump the content of raw NAND chip
24 24
25//applet:IF_NANDWRITE(APPLET(nandwrite, BB_DIR_USR_SBIN, BB_SUID_DROP)) 25//applet:IF_NANDWRITE(APPLET(nandwrite, BB_DIR_USR_SBIN, BB_SUID_DROP))
26//applet:IF_NANDWRITE(APPLET_ODDNAME(nanddump, nandwrite, BB_DIR_USR_SBIN, BB_SUID_DROP, nanddump)) 26//applet:IF_NANDDUMP(APPLET_ODDNAME(nanddump, nandwrite, BB_DIR_USR_SBIN, BB_SUID_DROP, nanddump))
27 27
28//kbuild:lib-$(CONFIG_NANDWRITE) += nandwrite.o 28//kbuild:lib-$(CONFIG_NANDWRITE) += nandwrite.o
29//kbuild:lib-$(CONFIG_NANDDUMP) += nandwrite.o 29//kbuild:lib-$(CONFIG_NANDDUMP) += nandwrite.o
@@ -31,14 +31,14 @@
31//usage:#define nandwrite_trivial_usage 31//usage:#define nandwrite_trivial_usage
32//usage: "[-p] [-s ADDR] MTD_DEVICE [FILE]" 32//usage: "[-p] [-s ADDR] MTD_DEVICE [FILE]"
33//usage:#define nandwrite_full_usage "\n\n" 33//usage:#define nandwrite_full_usage "\n\n"
34//usage: "Write to the specified MTD device\n" 34//usage: "Write to MTD_DEVICE\n"
35//usage: "\n -p Pad to page size" 35//usage: "\n -p Pad to page size"
36//usage: "\n -s ADDR Start address" 36//usage: "\n -s ADDR Start address"
37 37
38//usage:#define nanddump_trivial_usage 38//usage:#define nanddump_trivial_usage
39//usage: "[-o] [-b] [-s ADDR] [-f FILE] MTD_DEVICE" 39//usage: "[-o] [-b] [-s ADDR] [-l LEN] [-f FILE] MTD_DEVICE"
40//usage:#define nanddump_full_usage "\n\n" 40//usage:#define nanddump_full_usage "\n\n"
41//usage: "Dump the specified MTD device\n" 41//usage: "Dump MTD_DEVICE\n"
42//usage: "\n -o Dump oob data" 42//usage: "\n -o Dump oob data"
43//usage: "\n -b Omit bad block from the dump" 43//usage: "\n -b Omit bad block from the dump"
44//usage: "\n -s ADDR Start address" 44//usage: "\n -s ADDR Start address"
diff --git a/networking/ether-wake.c b/networking/ether-wake.c
index bf09cd529..2d389ea30 100644
--- a/networking/ether-wake.c
+++ b/networking/ether-wake.c
@@ -121,10 +121,7 @@ static void get_dest_addr(const char *hostid, struct ether_addr *eaddr)
121 eap = ether_aton_r(hostid, eaddr); 121 eap = ether_aton_r(hostid, eaddr);
122 if (eap) { 122 if (eap) {
123 bb_debug_msg("The target station address is %s\n\n", ether_ntoa(eap)); 123 bb_debug_msg("The target station address is %s\n\n", ether_ntoa(eap));
124#if !defined(__UCLIBC_MAJOR__) \ 124#if !defined(__UCLIBC__) || UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 30)
125 || __UCLIBC_MAJOR__ > 0 \
126 || __UCLIBC_MINOR__ > 9 \
127 || (__UCLIBC_MINOR__ == 9 && __UCLIBC_SUBLEVEL__ >= 30)
128 } else if (ether_hostton(hostid, eaddr) == 0) { 125 } else if (ether_hostton(hostid, eaddr) == 0) {
129 bb_debug_msg("Station address for hostname %s is %s\n\n", hostid, ether_ntoa(eaddr)); 126 bb_debug_msg("Station address for hostname %s is %s\n\n", hostid, ether_ntoa(eaddr));
130#endif 127#endif
diff --git a/networking/ifplugd.c b/networking/ifplugd.c
index 86586f0fe..3cdc2c9d2 100644
--- a/networking/ifplugd.c
+++ b/networking/ifplugd.c
@@ -556,7 +556,8 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv)
556 556
557 if (opts & FLAG_KILL) { 557 if (opts & FLAG_KILL) {
558 if (pid_from_pidfile > 0) 558 if (pid_from_pidfile > 0)
559 kill(pid_from_pidfile, SIGQUIT); 559 /* Upstream tool use SIGINT for -k */
560 kill(pid_from_pidfile, SIGINT);
560 return EXIT_SUCCESS; 561 return EXIT_SUCCESS;
561 } 562 }
562 563
diff --git a/networking/ifupdown.c b/networking/ifupdown.c
index 818048284..0f0857cb4 100644
--- a/networking/ifupdown.c
+++ b/networking/ifupdown.c
@@ -743,7 +743,7 @@ static const struct method_t *get_method(const struct address_family_t *af, char
743 return NULL; 743 return NULL;
744} 744}
745 745
746static struct interfaces_file_t *read_interfaces(const char *filename) 746static struct interfaces_file_t *read_interfaces(const char *filename, struct interfaces_file_t *defn)
747{ 747{
748 /* Let's try to be compatible. 748 /* Let's try to be compatible.
749 * 749 *
@@ -758,19 +758,25 @@ static struct interfaces_file_t *read_interfaces(const char *filename)
758 * be ignored. Blank lines are ignored. Lines may be indented freely. 758 * be ignored. Blank lines are ignored. Lines may be indented freely.
759 * A "\" character at the very end of the line indicates the next line 759 * A "\" character at the very end of the line indicates the next line
760 * should be treated as a continuation of the current one. 760 * should be treated as a continuation of the current one.
761 *
762 * Lines beginning with "source" are used to include stanzas from
763 * other files, so configuration can be split into many files.
764 * The word "source" is followed by the path of file to be sourced.
761 */ 765 */
762#if ENABLE_FEATURE_IFUPDOWN_MAPPING 766#if ENABLE_FEATURE_IFUPDOWN_MAPPING
763 struct mapping_defn_t *currmap = NULL; 767 struct mapping_defn_t *currmap = NULL;
764#endif 768#endif
765 struct interface_defn_t *currif = NULL; 769 struct interface_defn_t *currif = NULL;
766 struct interfaces_file_t *defn;
767 FILE *f; 770 FILE *f;
768 char *buf; 771 char *buf;
769 char *first_word; 772 char *first_word;
770 char *rest_of_line; 773 char *rest_of_line;
771 enum { NONE, IFACE, MAPPING } currently_processing = NONE; 774 enum { NONE, IFACE, MAPPING } currently_processing = NONE;
772 775
773 defn = xzalloc(sizeof(*defn)); 776 if (!defn)
777 defn = xzalloc(sizeof(*defn));
778
779 debug_noise("reading %s file:\n", filename);
774 f = xfopen_for_read(filename); 780 f = xfopen_for_read(filename);
775 781
776 while ((buf = xmalloc_fgetline(f)) != NULL) { 782 while ((buf = xmalloc_fgetline(f)) != NULL) {
@@ -881,6 +887,8 @@ static struct interfaces_file_t *read_interfaces(const char *filename)
881 debug_noise("\nauto %s\n", first_word); 887 debug_noise("\nauto %s\n", first_word);
882 } 888 }
883 currently_processing = NONE; 889 currently_processing = NONE;
890 } else if (strcmp(first_word, "source") == 0) {
891 read_interfaces(next_word(&rest_of_line), defn);
884 } else { 892 } else {
885 switch (currently_processing) { 893 switch (currently_processing) {
886 case IFACE: 894 case IFACE:
@@ -934,6 +942,7 @@ static struct interfaces_file_t *read_interfaces(const char *filename)
934 bb_error_msg_and_die("%s: I/O error", filename); 942 bb_error_msg_and_die("%s: I/O error", filename);
935 } 943 }
936 fclose(f); 944 fclose(f);
945 debug_noise("\ndone reading %s\n\n", filename);
937 946
938 return defn; 947 return defn;
939} 948}
@@ -1199,9 +1208,7 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv)
1199 if (!DO_ALL) bb_show_usage(); 1208 if (!DO_ALL) bb_show_usage();
1200 } 1209 }
1201 1210
1202 debug_noise("reading %s file:\n", interfaces); 1211 defn = read_interfaces(interfaces, NULL);
1203 defn = read_interfaces(interfaces);
1204 debug_noise("\ndone reading %s\n\n", interfaces);
1205 1212
1206 /* Create a list of interfaces to work on */ 1213 /* Create a list of interfaces to work on */
1207 if (DO_ALL) { 1214 if (DO_ALL) {
diff --git a/networking/nameif.c b/networking/nameif.c
index 5d7e8f9a4..9a8846dc0 100644
--- a/networking/nameif.c
+++ b/networking/nameif.c
@@ -292,12 +292,11 @@ int nameif_main(int argc UNUSED_PARAM, char **argv)
292 if (ch->mac && memcmp(ch->mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN) != 0) 292 if (ch->mac && memcmp(ch->mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN) != 0)
293 continue; 293 continue;
294 /* if we came here, all selectors have matched */ 294 /* if we came here, all selectors have matched */
295 break; 295 goto found;
296 } 296 }
297 /* Nothing found for current interface */ 297 /* Nothing found for current interface */
298 if (!ch) 298 continue;
299 continue; 299 found:
300
301 if (strcmp(ifr.ifr_name, ch->ifname) != 0) { 300 if (strcmp(ifr.ifr_name, ch->ifname) != 0) {
302 strcpy(ifr.ifr_newname, ch->ifname); 301 strcpy(ifr.ifr_newname, ch->ifname);
303 ioctl_or_perror_and_die(ctl_sk, SIOCSIFNAME, &ifr, 302 ioctl_or_perror_and_die(ctl_sk, SIOCSIFNAME, &ifr,
@@ -313,10 +312,14 @@ int nameif_main(int argc UNUSED_PARAM, char **argv)
313 ch->next->prev = ch->prev; 312 ch->next->prev = ch->prev;
314 if (ENABLE_FEATURE_CLEAN_UP) 313 if (ENABLE_FEATURE_CLEAN_UP)
315 delete_eth_table(ch); 314 delete_eth_table(ch);
316 } 315 } /* while */
316
317 if (ENABLE_FEATURE_CLEAN_UP) { 317 if (ENABLE_FEATURE_CLEAN_UP) {
318 for (ch = clist; ch; ch = ch->next) 318 ethtable_t *next;
319 for (ch = clist; ch; ch = next) {
320 next = ch->next;
319 delete_eth_table(ch); 321 delete_eth_table(ch);
322 }
320 config_close(parser); 323 config_close(parser);
321 }; 324 };
322 325
diff --git a/networking/nc.c b/networking/nc.c
index 0c843a686..126bec906 100644
--- a/networking/nc.c
+++ b/networking/nc.c
@@ -24,7 +24,7 @@
24//config: Allow netcat to act as a server. 24//config: Allow netcat to act as a server.
25//config: 25//config:
26//config:config NC_EXTRA 26//config:config NC_EXTRA
27//config: bool "Netcat extensions (-eiw and filename)" 27//config: bool "Netcat extensions (-eiw and -f FILE)"
28//config: default y 28//config: default y
29//config: depends on NC 29//config: depends on NC
30//config: help 30//config: help
@@ -40,7 +40,7 @@
40//config: This option makes nc closely follow original nc-1.10. 40//config: This option makes nc closely follow original nc-1.10.
41//config: The code is about 2.5k bigger. It enables 41//config: The code is about 2.5k bigger. It enables
42//config: -s ADDR, -n, -u, -v, -o FILE, -z options, but loses 42//config: -s ADDR, -n, -u, -v, -o FILE, -z options, but loses
43//config: busybox-specific extensions: -f FILE and -ll. 43//config: busybox-specific extensions: -f FILE.
44 44
45#if ENABLE_NC_110_COMPAT 45#if ENABLE_NC_110_COMPAT
46# include "nc_bloaty.c" 46# include "nc_bloaty.c"
@@ -60,17 +60,18 @@
60//usage:#define nc_full_usage "\n\n" 60//usage:#define nc_full_usage "\n\n"
61//usage: "Open a pipe to IP:PORT" IF_NC_EXTRA(" or FILE") 61//usage: "Open a pipe to IP:PORT" IF_NC_EXTRA(" or FILE")
62//usage: NC_OPTIONS_STR 62//usage: NC_OPTIONS_STR
63//usage: IF_NC_EXTRA(
64//usage: "\n -e PROG Run PROG after connect"
65//usage: IF_NC_SERVER( 63//usage: IF_NC_SERVER(
66//usage: "\n -l Listen mode, for inbound connects" 64//usage: "\n -l Listen mode, for inbound connects"
67//usage: IF_NC_EXTRA( 65//usage: IF_NC_EXTRA(
68//usage: "\n (use -l twice with -e for persistent server)") 66//usage: "\n (use -ll with -e for persistent server)"
67//usage: )
69//usage: "\n -p PORT Local port" 68//usage: "\n -p PORT Local port"
70//usage: ) 69//usage: )
71//usage: "\n -w SEC Timeout for connect" 70//usage: IF_NC_EXTRA(
71//usage: "\n -w SEC Connect timeout"
72//usage: "\n -i SEC Delay interval for lines sent" 72//usage: "\n -i SEC Delay interval for lines sent"
73//usage: "\n -f FILE Use file (ala /dev/ttyS0) instead of network" 73//usage: "\n -f FILE Use file (ala /dev/ttyS0) instead of network"
74//usage: "\n -e PROG Run PROG after connect"
74//usage: ) 75//usage: )
75//usage: 76//usage:
76//usage:#define nc_notes_usage "" 77//usage:#define nc_notes_usage ""
@@ -147,7 +148,7 @@ int nc_main(int argc, char **argv)
147 *p++ = argv[optind++]; 148 *p++ = argv[optind++];
148 } 149 }
149 ) 150 )
150 /* optind points to argv[arvc] (NULL) now. 151 /* optind points to argv[argc] (NULL) now.
151 ** FIXME: we assume that getopt will not count options 152 ** FIXME: we assume that getopt will not count options
152 ** possibly present on "-e PROG ARGS" and will not 153 ** possibly present on "-e PROG ARGS" and will not
153 ** include them into final value of optind 154 ** include them into final value of optind
@@ -226,10 +227,9 @@ int nc_main(int argc, char **argv)
226 /* child, or main thread if only one -l */ 227 /* child, or main thread if only one -l */
227 xmove_fd(cfd, 0); 228 xmove_fd(cfd, 0);
228 xdup2(0, 1); 229 xdup2(0, 1);
229 xdup2(0, 2); 230 /*xdup2(0, 2); - original nc 1.10 does this, we don't */
230 IF_NC_EXTRA(BB_EXECVP(execparam[0], execparam);) 231 IF_NC_EXTRA(BB_EXECVP(execparam[0], execparam);)
231 /* Don't print stuff or it will go over the wire... */ 232 IF_NC_EXTRA(bb_perror_msg_and_die("can't execute '%s'", execparam[0]);)
232 _exit(127);
233 } 233 }
234 234
235 /* Select loop copying stdin to cfd, and cfd to stdout */ 235 /* Select loop copying stdin to cfd, and cfd to stdout */
diff --git a/networking/nc_bloaty.c b/networking/nc_bloaty.c
index 62a025116..00ba6f114 100644
--- a/networking/nc_bloaty.c
+++ b/networking/nc_bloaty.c
@@ -63,6 +63,12 @@
63//usage: " -e PROG Run PROG after connect (must be last)" 63//usage: " -e PROG Run PROG after connect (must be last)"
64//usage: IF_NC_SERVER( 64//usage: IF_NC_SERVER(
65//usage: "\n -l Listen mode, for inbound connects" 65//usage: "\n -l Listen mode, for inbound connects"
66//usage: "\n -lk With -e, provides persistent server"
67/* -ll does the same as -lk, but its our extension, while -k is BSD'd,
68 * presumably more widely known. Therefore we advertise it, not -ll.
69 * I would like to drop -ll support, but our "small" nc supports it,
70 * and Rob uses it.
71 */
66//usage: ) 72//usage: )
67//usage: "\n -p PORT Local port" 73//usage: "\n -p PORT Local port"
68//usage: "\n -s ADDR Local address" 74//usage: "\n -s ADDR Local address"
@@ -166,18 +172,14 @@ enum {
166 OPT_v = (1 << 4), 172 OPT_v = (1 << 4),
167 OPT_w = (1 << 5), 173 OPT_w = (1 << 5),
168 OPT_l = (1 << 6) * ENABLE_NC_SERVER, 174 OPT_l = (1 << 6) * ENABLE_NC_SERVER,
169 OPT_i = (1 << (6+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA, 175 OPT_k = (1 << 7) * ENABLE_NC_SERVER,
170 OPT_o = (1 << (7+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA, 176 OPT_i = (1 << (7+2*ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
171 OPT_z = (1 << (8+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA, 177 OPT_o = (1 << (8+2*ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
178 OPT_z = (1 << (9+2*ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
172}; 179};
173 180
174#define o_nflag (option_mask32 & OPT_n) 181#define o_nflag (option_mask32 & OPT_n)
175#define o_udpmode (option_mask32 & OPT_u) 182#define o_udpmode (option_mask32 & OPT_u)
176#if ENABLE_NC_SERVER
177#define o_listen (option_mask32 & OPT_l)
178#else
179#define o_listen 0
180#endif
181#if ENABLE_NC_EXTRA 183#if ENABLE_NC_EXTRA
182#define o_ofile (option_mask32 & OPT_o) 184#define o_ofile (option_mask32 & OPT_o)
183#define o_zero (option_mask32 & OPT_z) 185#define o_zero (option_mask32 & OPT_z)
@@ -298,7 +300,7 @@ static int connect_w_timeout(int fd)
298 incoming and returns an open connection *from* someplace. If we were 300 incoming and returns an open connection *from* someplace. If we were
299 given host/port args, any connections from elsewhere are rejected. This 301 given host/port args, any connections from elsewhere are rejected. This
300 in conjunction with local-address binding should limit things nicely... */ 302 in conjunction with local-address binding should limit things nicely... */
301static void dolisten(void) 303static void dolisten(int is_persistent, char **proggie)
302{ 304{
303 int rr; 305 int rr;
304 306
@@ -371,6 +373,7 @@ create new one, and bind() it. TODO */
371 xconnect(netfd, &remend.u.sa, ouraddr->len); 373 xconnect(netfd, &remend.u.sa, ouraddr->len);
372 } else { 374 } else {
373 /* TCP */ 375 /* TCP */
376 another:
374 arm(o_wait); /* wrap this in a timer, too; 0 = forever */ 377 arm(o_wait); /* wrap this in a timer, too; 0 = forever */
375 if (setjmp(jbuf) == 0) { 378 if (setjmp(jbuf) == 0) {
376 again: 379 again:
@@ -405,6 +408,19 @@ create new one, and bind() it. TODO */
405 unarm(); 408 unarm();
406 } else 409 } else
407 bb_error_msg_and_die("timeout"); 410 bb_error_msg_and_die("timeout");
411
412 if (is_persistent && proggie) {
413 /* -l -k -e PROG */
414 signal(SIGCHLD, SIG_IGN); /* no zombies please */
415 if (xvfork() != 0) {
416 /* parent: go back and accept more connections */
417 close(rr);
418 goto another;
419 }
420 /* child */
421 signal(SIGCHLD, SIG_DFL);
422 }
423
408 xmove_fd(rr, netfd); /* dump the old socket, here's our new one */ 424 xmove_fd(rr, netfd); /* dump the old socket, here's our new one */
409 /* find out what address the connection was *to* on our end, in case we're 425 /* find out what address the connection was *to* on our end, in case we're
410 doing a listen-on-any on a multihomed machine. This allows one to 426 doing a listen-on-any on a multihomed machine. This allows one to
@@ -454,6 +470,9 @@ create new one, and bind() it. TODO */
454 if (!o_nflag) 470 if (!o_nflag)
455 free(remhostname); 471 free(remhostname);
456 } 472 }
473
474 if (proggie)
475 doexec(proggie);
457} 476}
458 477
459/* udptest: 478/* udptest:
@@ -730,6 +749,7 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
730 char *themdotted = themdotted; /* for compiler */ 749 char *themdotted = themdotted; /* for compiler */
731 char **proggie; 750 char **proggie;
732 int x; 751 int x;
752 unsigned cnt_l = 0;
733 unsigned o_lport = 0; 753 unsigned o_lport = 0;
734 754
735 INIT_G(); 755 INIT_G();
@@ -760,7 +780,7 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
760 if (proggie[0][0] == '-') { 780 if (proggie[0][0] == '-') {
761 char *optpos = *proggie + 1; 781 char *optpos = *proggie + 1;
762 /* Skip all valid opts w/o params */ 782 /* Skip all valid opts w/o params */
763 optpos = optpos + strspn(optpos, "nuv"IF_NC_SERVER("l")IF_NC_EXTRA("z")); 783 optpos = optpos + strspn(optpos, "nuv"IF_NC_SERVER("lk")IF_NC_EXTRA("z"));
764 if (*optpos == 'e' && !optpos[1]) { 784 if (*optpos == 'e' && !optpos[1]) {
765 *optpos = '\0'; 785 *optpos = '\0';
766 proggie++; 786 proggie++;
@@ -774,17 +794,21 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
774 e_found: 794 e_found:
775 795
776 // -g -G -t -r deleted, unimplemented -a deleted too 796 // -g -G -t -r deleted, unimplemented -a deleted too
777 opt_complementary = "?2:vv:w+"; /* max 2 params; -v is a counter; -w N */ 797 opt_complementary = "?2:vv:ll:w+"; /* max 2 params; -v and -l are counters; -w N */
778 getopt32(argv, "np:s:uvw:" IF_NC_SERVER("l") 798 getopt32(argv, "np:s:uvw:" IF_NC_SERVER("lk")
779 IF_NC_EXTRA("i:o:z"), 799 IF_NC_EXTRA("i:o:z"),
780 &str_p, &str_s, &o_wait 800 &str_p, &str_s, &o_wait
781 IF_NC_EXTRA(, &str_i, &str_o), &o_verbose); 801 IF_NC_EXTRA(, &str_i, &str_o), &o_verbose IF_NC_SERVER(, &cnt_l));
782 argv += optind; 802 argv += optind;
783#if ENABLE_NC_EXTRA 803#if ENABLE_NC_EXTRA
784 if (option_mask32 & OPT_i) /* line-interval time */ 804 if (option_mask32 & OPT_i) /* line-interval time */
785 o_interval = xatou_range(str_i, 1, 0xffff); 805 o_interval = xatou_range(str_i, 1, 0xffff);
786#endif 806#endif
807#if ENABLE_NC_SERVER
787 //if (option_mask32 & OPT_l) /* listen mode */ 808 //if (option_mask32 & OPT_l) /* listen mode */
809 if (option_mask32 & OPT_k) /* persistent server mode */
810 cnt_l = 2;
811#endif
788 //if (option_mask32 & OPT_n) /* numeric-only, no DNS lookups */ 812 //if (option_mask32 & OPT_n) /* numeric-only, no DNS lookups */
789 //if (option_mask32 & OPT_o) /* hexdump log */ 813 //if (option_mask32 & OPT_o) /* hexdump log */
790 if (option_mask32 & OPT_p) { /* local source port */ 814 if (option_mask32 & OPT_p) { /* local source port */
@@ -833,7 +857,7 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
833 if (o_udpmode) 857 if (o_udpmode)
834 socket_want_pktinfo(netfd); 858 socket_want_pktinfo(netfd);
835 if (!ENABLE_FEATURE_UNIX_LOCAL 859 if (!ENABLE_FEATURE_UNIX_LOCAL
836 || o_listen 860 || cnt_l != 0 /* listen */
837 || ouraddr->u.sa.sa_family != AF_UNIX 861 || ouraddr->u.sa.sa_family != AF_UNIX
838 ) { 862 ) {
839 xbind(netfd, &ouraddr->u.sa, ouraddr->len); 863 xbind(netfd, &ouraddr->u.sa, ouraddr->len);
@@ -862,11 +886,9 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
862 xmove_fd(xopen(str_o, O_WRONLY|O_CREAT|O_TRUNC), ofd); 886 xmove_fd(xopen(str_o, O_WRONLY|O_CREAT|O_TRUNC), ofd);
863#endif 887#endif
864 888
865 if (o_listen) { 889 if (cnt_l != 0) {
866 dolisten(); 890 dolisten((cnt_l - 1), proggie);
867 /* dolisten does its own connect reporting */ 891 /* dolisten does its own connect reporting */
868 if (proggie) /* -e given? */
869 doexec(proggie);
870 x = readwrite(); /* it even works with UDP! */ 892 x = readwrite(); /* it even works with UDP! */
871 } else { 893 } else {
872 /* Outbound connects. Now we're more picky about args... */ 894 /* Outbound connects. Now we're more picky about args... */
diff --git a/networking/ping.c b/networking/ping.c
index 3df67f5c3..e919b3a09 100644
--- a/networking/ping.c
+++ b/networking/ping.c
@@ -89,7 +89,9 @@
89//usage: "[OPTIONS] HOST" 89//usage: "[OPTIONS] HOST"
90//usage:# define ping_full_usage "\n\n" 90//usage:# define ping_full_usage "\n\n"
91//usage: "Send ICMP ECHO_REQUEST packets to network hosts\n" 91//usage: "Send ICMP ECHO_REQUEST packets to network hosts\n"
92//usage: IF_PING6(
92//usage: "\n -4,-6 Force IP or IPv6 name resolution" 93//usage: "\n -4,-6 Force IP or IPv6 name resolution"
94//usage: )
93//usage: "\n -c CNT Send only CNT pings" 95//usage: "\n -c CNT Send only CNT pings"
94//usage: "\n -s SIZE Send SIZE data bytes in packets (default:56)" 96//usage: "\n -s SIZE Send SIZE data bytes in packets (default:56)"
95//usage: "\n -t TTL Set TTL" 97//usage: "\n -t TTL Set TTL"
@@ -299,7 +301,7 @@ static int common_ping_main(sa_family_t af, char **argv)
299 301
300/* Full(er) version */ 302/* Full(er) version */
301 303
302#define OPT_STRING ("qvc:s:t:w:W:I:4" IF_PING6("6")) 304#define OPT_STRING ("qvc:s:t:w:W:I:n4" IF_PING6("6"))
303enum { 305enum {
304 OPT_QUIET = 1 << 0, 306 OPT_QUIET = 1 << 0,
305 OPT_VERBOSE = 1 << 1, 307 OPT_VERBOSE = 1 << 1,
@@ -309,8 +311,9 @@ enum {
309 OPT_w = 1 << 5, 311 OPT_w = 1 << 5,
310 OPT_W = 1 << 6, 312 OPT_W = 1 << 6,
311 OPT_I = 1 << 7, 313 OPT_I = 1 << 7,
312 OPT_IPV4 = 1 << 8, 314 /*OPT_n = 1 << 8, - ignored */
313 OPT_IPV6 = (1 << 9) * ENABLE_PING6, 315 OPT_IPV4 = 1 << 9,
316 OPT_IPV6 = (1 << 10) * ENABLE_PING6,
314}; 317};
315 318
316 319
@@ -349,9 +352,6 @@ struct globals {
349#define source_lsa (G.source_lsa ) 352#define source_lsa (G.source_lsa )
350#define str_I (G.str_I ) 353#define str_I (G.str_I )
351#define datalen (G.datalen ) 354#define datalen (G.datalen )
352#define ntransmitted (G.ntransmitted)
353#define nreceived (G.nreceived )
354#define nrepeats (G.nrepeats )
355#define pingcount (G.pingcount ) 355#define pingcount (G.pingcount )
356#define opt_ttl (G.opt_ttl ) 356#define opt_ttl (G.opt_ttl )
357#define myid (G.myid ) 357#define myid (G.myid )
@@ -387,33 +387,40 @@ void BUG_ping_globals_too_big(void);
387static void print_stats_and_exit(int junk) NORETURN; 387static void print_stats_and_exit(int junk) NORETURN;
388static void print_stats_and_exit(int junk UNUSED_PARAM) 388static void print_stats_and_exit(int junk UNUSED_PARAM)
389{ 389{
390 unsigned long ul;
391 unsigned long nrecv;
392
390 signal(SIGINT, SIG_IGN); 393 signal(SIGINT, SIG_IGN);
391 394
392 printf("\n--- %s ping statistics ---\n", hostname); 395 nrecv = G.nreceived;
393 printf("%lu packets transmitted, ", ntransmitted); 396 printf("\n--- %s ping statistics ---\n"
394 printf("%lu packets received, ", nreceived); 397 "%lu packets transmitted, "
395 if (nrepeats) 398 "%lu packets received, ",
396 printf("%lu duplicates, ", nrepeats); 399 hostname, G.ntransmitted, nrecv
397 if (ntransmitted) 400 );
398 ntransmitted = (ntransmitted - nreceived) * 100 / ntransmitted; 401 if (G.nrepeats)
399 printf("%lu%% packet loss\n", ntransmitted); 402 printf("%lu duplicates, ", G.nrepeats);
403 ul = G.ntransmitted;
404 if (ul != 0)
405 ul = (ul - nrecv) * 100 / ul;
406 printf("%lu%% packet loss\n", ul);
400 if (tmin != UINT_MAX) { 407 if (tmin != UINT_MAX) {
401 unsigned tavg = tsum / (nreceived + nrepeats); 408 unsigned tavg = tsum / (nrecv + G.nrepeats);
402 printf("round-trip min/avg/max = %u.%03u/%u.%03u/%u.%03u ms\n", 409 printf("round-trip min/avg/max = %u.%03u/%u.%03u/%u.%03u ms\n",
403 tmin / 1000, tmin % 1000, 410 tmin / 1000, tmin % 1000,
404 tavg / 1000, tavg % 1000, 411 tavg / 1000, tavg % 1000,
405 tmax / 1000, tmax % 1000); 412 tmax / 1000, tmax % 1000);
406 } 413 }
407 /* if condition is true, exit with 1 -- 'failure' */ 414 /* if condition is true, exit with 1 -- 'failure' */
408 exit(nreceived == 0 || (deadline && nreceived < pingcount)); 415 exit(nrecv == 0 || (deadline && nrecv < pingcount));
409} 416}
410 417
411static void sendping_tail(void (*sp)(int), int size_pkt) 418static void sendping_tail(void (*sp)(int), int size_pkt)
412{ 419{
413 int sz; 420 int sz;
414 421
415 CLR((uint16_t)ntransmitted % MAX_DUP_CHK); 422 CLR((uint16_t)G.ntransmitted % MAX_DUP_CHK);
416 ntransmitted++; 423 G.ntransmitted++;
417 424
418 size_pkt += datalen; 425 size_pkt += datalen;
419 426
@@ -423,7 +430,7 @@ static void sendping_tail(void (*sp)(int), int size_pkt)
423 if (sz != size_pkt) 430 if (sz != size_pkt)
424 bb_error_msg_and_die(bb_msg_write_error); 431 bb_error_msg_and_die(bb_msg_write_error);
425 432
426 if (pingcount == 0 || deadline || ntransmitted < pingcount) { 433 if (pingcount == 0 || deadline || G.ntransmitted < pingcount) {
427 /* Didn't send all pings yet - schedule next in 1s */ 434 /* Didn't send all pings yet - schedule next in 1s */
428 signal(SIGALRM, sp); 435 signal(SIGALRM, sp);
429 if (deadline) { 436 if (deadline) {
@@ -439,7 +446,7 @@ static void sendping_tail(void (*sp)(int), int size_pkt)
439 * otherwise ping waits for two RTTs. */ 446 * otherwise ping waits for two RTTs. */
440 unsigned expire = timeout; 447 unsigned expire = timeout;
441 448
442 if (nreceived) { 449 if (G.nreceived) {
443 /* approx. 2*tmax, in seconds (2 RTT) */ 450 /* approx. 2*tmax, in seconds (2 RTT) */
444 expire = tmax / (512*1024); 451 expire = tmax / (512*1024);
445 if (expire == 0) 452 if (expire == 0)
@@ -458,7 +465,7 @@ static void sendping4(int junk UNUSED_PARAM)
458 pkt->icmp_type = ICMP_ECHO; 465 pkt->icmp_type = ICMP_ECHO;
459 /*pkt->icmp_code = 0;*/ 466 /*pkt->icmp_code = 0;*/
460 pkt->icmp_cksum = 0; /* cksum is calculated with this field set to 0 */ 467 pkt->icmp_cksum = 0; /* cksum is calculated with this field set to 0 */
461 pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */ 468 pkt->icmp_seq = htons(G.ntransmitted); /* don't ++ here, it can be a macro */
462 pkt->icmp_id = myid; 469 pkt->icmp_id = myid;
463 470
464 /* If datalen < 4, we store timestamp _past_ the packet, 471 /* If datalen < 4, we store timestamp _past_ the packet,
@@ -481,7 +488,7 @@ static void sendping6(int junk UNUSED_PARAM)
481 pkt->icmp6_type = ICMP6_ECHO_REQUEST; 488 pkt->icmp6_type = ICMP6_ECHO_REQUEST;
482 /*pkt->icmp6_code = 0;*/ 489 /*pkt->icmp6_code = 0;*/
483 /*pkt->icmp6_cksum = 0;*/ 490 /*pkt->icmp6_cksum = 0;*/
484 pkt->icmp6_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */ 491 pkt->icmp6_seq = htons(G.ntransmitted); /* don't ++ here, it can be a macro */
485 pkt->icmp6_id = myid; 492 pkt->icmp6_id = myid;
486 493
487 /*if (datalen >= 4)*/ 494 /*if (datalen >= 4)*/
@@ -548,7 +555,7 @@ static void unpack_tail(int sz, uint32_t *tp,
548 const char *dupmsg = " (DUP!)"; 555 const char *dupmsg = " (DUP!)";
549 unsigned triptime = triptime; /* for gcc */ 556 unsigned triptime = triptime; /* for gcc */
550 557
551 ++nreceived; 558 ++G.nreceived;
552 559
553 if (tp) { 560 if (tp) {
554 /* (int32_t) cast is for hypothetical 64-bit unsigned */ 561 /* (int32_t) cast is for hypothetical 64-bit unsigned */
@@ -562,8 +569,8 @@ static void unpack_tail(int sz, uint32_t *tp,
562 } 569 }
563 570
564 if (TST(recv_seq % MAX_DUP_CHK)) { 571 if (TST(recv_seq % MAX_DUP_CHK)) {
565 ++nrepeats; 572 ++G.nrepeats;
566 --nreceived; 573 --G.nreceived;
567 } else { 574 } else {
568 SET(recv_seq % MAX_DUP_CHK); 575 SET(recv_seq % MAX_DUP_CHK);
569 dupmsg += 7; 576 dupmsg += 7;
@@ -692,7 +699,7 @@ static void ping4(len_and_sockaddr *lsa)
692 continue; 699 continue;
693 } 700 }
694 unpack4(G.rcv_packet, c, &from); 701 unpack4(G.rcv_packet, c, &from);
695 if (pingcount && nreceived >= pingcount) 702 if (pingcount && G.nreceived >= pingcount)
696 break; 703 break;
697 } 704 }
698} 705}
@@ -784,7 +791,7 @@ static void ping6(len_and_sockaddr *lsa)
784 } 791 }
785 } 792 }
786 unpack6(G.rcv_packet, c, &from, hoplimit); 793 unpack6(G.rcv_packet, c, &from, hoplimit);
787 if (pingcount && nreceived >= pingcount) 794 if (pingcount && G.nreceived >= pingcount)
788 break; 795 break;
789 } 796 }
790} 797}
diff --git a/networking/traceroute.c b/networking/traceroute.c
index 6b7b2ebdd..0c18d6c0c 100644
--- a/networking/traceroute.c
+++ b/networking/traceroute.c
@@ -805,6 +805,7 @@ common_traceroute_main(int op, char **argv)
805 char *waittime_str; 805 char *waittime_str;
806 char *pausemsecs_str; 806 char *pausemsecs_str;
807 char *first_ttl_str; 807 char *first_ttl_str;
808 char *dest_str;
808#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE 809#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
809 llist_t *source_route_list = NULL; 810 llist_t *source_route_list = NULL;
810 int lsrr = 0; 811 int lsrr = 0;
@@ -1059,8 +1060,12 @@ common_traceroute_main(int op, char **argv)
1059 xsetgid(getgid()); 1060 xsetgid(getgid());
1060 xsetuid(getuid()); 1061 xsetuid(getuid());
1061 1062
1062 printf("traceroute to %s (%s)", argv[0], 1063 dest_str = xmalloc_sockaddr2dotted_noport(&dest_lsa->u.sa);
1063 xmalloc_sockaddr2dotted_noport(&dest_lsa->u.sa)); 1064 printf("traceroute to %s (%s)", argv[0], dest_str);
1065 if (ENABLE_FEATURE_CLEAN_UP) {
1066 free(dest_str);
1067 }
1068
1064 if (op & OPT_SOURCE) 1069 if (op & OPT_SOURCE)
1065 printf(" from %s", source); 1070 printf(" from %s", source);
1066 printf(", %d hops max, %d byte packets\n", max_ttl, packlen); 1071 printf(", %d hops max, %d byte packets\n", max_ttl, packlen);
@@ -1216,6 +1221,12 @@ common_traceroute_main(int op, char **argv)
1216 } 1221 }
1217 } 1222 }
1218 1223
1224 if (ENABLE_FEATURE_CLEAN_UP) {
1225 free(to);
1226 free(lastaddr);
1227 free(from_lsa);
1228 }
1229
1219 return 0; 1230 return 0;
1220} 1231}
1221 1232
diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c
index 9ad95954d..a1a7f6b57 100644
--- a/networking/udhcp/dhcpd.c
+++ b/networking/udhcp/dhcpd.c
@@ -22,11 +22,12 @@
22 */ 22 */
23 23
24//usage:#define udhcpd_trivial_usage 24//usage:#define udhcpd_trivial_usage
25//usage: "[-fS]" IF_FEATURE_UDHCP_PORT(" [-P N]") " [CONFFILE]" 25//usage: "[-fS] [-I ADDR]" IF_FEATURE_UDHCP_PORT(" [-P N]") " [CONFFILE]"
26//usage:#define udhcpd_full_usage "\n\n" 26//usage:#define udhcpd_full_usage "\n\n"
27//usage: "DHCP server\n" 27//usage: "DHCP server\n"
28//usage: "\n -f Run in foreground" 28//usage: "\n -f Run in foreground"
29//usage: "\n -S Log to syslog too" 29//usage: "\n -S Log to syslog too"
30//usage: "\n -I ADDR Local address"
30//usage: IF_FEATURE_UDHCP_PORT( 31//usage: IF_FEATURE_UDHCP_PORT(
31//usage: "\n -P N Use port N (default 67)" 32//usage: "\n -P N Use port N (default 67)"
32//usage: ) 33//usage: )
@@ -302,6 +303,7 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
302 unsigned num_ips; 303 unsigned num_ips;
303 unsigned opt; 304 unsigned opt;
304 struct option_set *option; 305 struct option_set *option;
306 char *str_I = str_I;
305 IF_FEATURE_UDHCP_PORT(char *str_P;) 307 IF_FEATURE_UDHCP_PORT(char *str_P;)
306 308
307#if ENABLE_FEATURE_UDHCP_PORT 309#if ENABLE_FEATURE_UDHCP_PORT
@@ -312,8 +314,10 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
312#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 314#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
313 opt_complementary = "vv"; 315 opt_complementary = "vv";
314#endif 316#endif
315 opt = getopt32(argv, "fSv" 317 opt = getopt32(argv, "fSI:v"
316 IF_FEATURE_UDHCP_PORT("P:", &str_P) 318 IF_FEATURE_UDHCP_PORT("P:")
319 , &str_I
320 IF_FEATURE_UDHCP_PORT(, &str_P)
317 IF_UDHCP_VERBOSE(, &dhcp_verbose) 321 IF_UDHCP_VERBOSE(, &dhcp_verbose)
318 ); 322 );
319 if (!(opt & 1)) { /* no -f */ 323 if (!(opt & 1)) { /* no -f */
@@ -326,8 +330,13 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
326 openlog(applet_name, LOG_PID, LOG_DAEMON); 330 openlog(applet_name, LOG_PID, LOG_DAEMON);
327 logmode |= LOGMODE_SYSLOG; 331 logmode |= LOGMODE_SYSLOG;
328 } 332 }
333 if (opt & 4) { /* -I */
334 len_and_sockaddr *lsa = xhost_and_af2sockaddr(str_I, 0, AF_INET);
335 server_config.server_nip = lsa->u.sin.sin_addr.s_addr;
336 free(lsa);
337 }
329#if ENABLE_FEATURE_UDHCP_PORT 338#if ENABLE_FEATURE_UDHCP_PORT
330 if (opt & 8) { /* -P */ 339 if (opt & 16) { /* -P */
331 SERVER_PORT = xatou16(str_P); 340 SERVER_PORT = xatou16(str_P);
332 CLIENT_PORT = SERVER_PORT + 1; 341 CLIENT_PORT = SERVER_PORT + 1;
333 } 342 }
@@ -367,7 +376,7 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
367 376
368 if (udhcp_read_interface(server_config.interface, 377 if (udhcp_read_interface(server_config.interface,
369 &server_config.ifindex, 378 &server_config.ifindex,
370 &server_config.server_nip, 379 (server_config.server_nip == 0 ? &server_config.server_nip : NULL),
371 server_config.server_mac) 380 server_config.server_mac)
372 ) { 381 ) {
373 retval = 1; 382 retval = 1;
diff --git a/procps/pgrep.c b/procps/pgrep.c
index dc7ffff48..8daf5b28a 100644
--- a/procps/pgrep.c
+++ b/procps/pgrep.c
@@ -128,7 +128,7 @@ int pgrep_main(int argc UNUSED_PARAM, char **argv)
128 bb_show_usage(); 128 bb_show_usage();
129 129
130 if (argv[0]) 130 if (argv[0])
131 xregcomp(&re_buffer, argv[0], REG_EXTENDED | REG_NOSUB); 131 xregcomp(&re_buffer, argv[0], OPT_ANCHOR ? REG_EXTENDED : (REG_EXTENDED|REG_NOSUB));
132 132
133 matched_pid = 0; 133 matched_pid = 0;
134 cmd_last = NULL; 134 cmd_last = NULL;
diff --git a/shell/ash.c b/shell/ash.c
index 75c01ec14..962fc2609 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -441,6 +441,9 @@ static void trace_vprintf(const char *fmt, va_list va);
441/* ============ Utility functions */ 441/* ============ Utility functions */
442#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0) 442#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
443 443
444#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
445#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
446
444static int isdigit_str9(const char *str) 447static int isdigit_str9(const char *str)
445{ 448{
446 int maxlen = 9 + 1; /* max 9 digits: 999999999 */ 449 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
@@ -2064,27 +2067,6 @@ getoptsreset(const char *value)
2064} 2067}
2065#endif 2068#endif
2066 2069
2067/* math.h has these, otherwise define our private copies */
2068#if !ENABLE_SH_MATH_SUPPORT
2069#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
2070#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
2071/*
2072 * Return the pointer to the first char which is not part of a legal variable name
2073 * (a letter or underscore followed by letters, underscores, and digits).
2074 */
2075static const char*
2076endofname(const char *name)
2077{
2078 if (!is_name(*name))
2079 return name;
2080 while (*++name) {
2081 if (!is_in_name(*name))
2082 break;
2083 }
2084 return name;
2085}
2086#endif
2087
2088/* 2070/*
2089 * Compares two strings up to the first = or '\0'. The first 2071 * Compares two strings up to the first = or '\0'. The first
2090 * string must be terminated by '='; the second may be terminated by 2072 * string must be terminated by '='; the second may be terminated by
@@ -12774,6 +12756,9 @@ dotcmd(int argc, char **argv)
12774 /* "false; . empty_file; echo $?" should print 0, not 1: */ 12756 /* "false; . empty_file; echo $?" should print 0, not 1: */
12775 exitstatus = 0; 12757 exitstatus = 0;
12776 12758
12759 /* This aborts if file isn't found, which is POSIXly correct.
12760 * bash returns exitcode 1 instead.
12761 */
12777 fullname = find_dot_file(argv[1]); 12762 fullname = find_dot_file(argv[1]);
12778 12763
12779 argv += 2; 12764 argv += 2;
@@ -12785,6 +12770,9 @@ dotcmd(int argc, char **argv)
12785 shellparam.p = argv; 12770 shellparam.p = argv;
12786 }; 12771 };
12787 12772
12773 /* This aborts if file can't be opened, which is POSIXly correct.
12774 * bash returns exitcode 1 instead.
12775 */
12788 setinputfile(fullname, INPUT_PUSH_FILE); 12776 setinputfile(fullname, INPUT_PUSH_FILE);
12789 commandname = fullname; 12777 commandname = fullname;
12790 cmdloop(0); 12778 cmdloop(0);
@@ -13832,27 +13820,21 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
13832#endif 13820#endif
13833 procargs(argv); 13821 procargs(argv);
13834 13822
13835#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13836 if (iflag) {
13837 const char *hp = lookupvar("HISTFILE");
13838 if (!hp) {
13839 hp = lookupvar("HOME");
13840 if (hp) {
13841 char *defhp = concat_path_file(hp, ".ash_history");
13842 setvar("HISTFILE", defhp, 0);
13843 free(defhp);
13844 }
13845 }
13846 }
13847#endif
13848 if (argv[0] && argv[0][0] == '-') 13823 if (argv[0] && argv[0][0] == '-')
13849 isloginsh = 1; 13824 isloginsh = 1;
13850 if (isloginsh) { 13825 if (isloginsh) {
13826 const char *hp;
13827
13851 state = 1; 13828 state = 1;
13852 read_profile("/etc/profile"); 13829 read_profile("/etc/profile");
13853 state1: 13830 state1:
13854 state = 2; 13831 state = 2;
13855 read_profile(".profile"); 13832 hp = lookupvar("HOME");
13833 if (hp) {
13834 hp = concat_path_file(hp, ".profile");
13835 read_profile(hp);
13836 free((char*)hp);
13837 }
13856 } 13838 }
13857 state2: 13839 state2:
13858 state = 3; 13840 state = 3;
@@ -13884,6 +13866,15 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
13884#if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY 13866#if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
13885 if (iflag) { 13867 if (iflag) {
13886 const char *hp = lookupvar("HISTFILE"); 13868 const char *hp = lookupvar("HISTFILE");
13869 if (!hp) {
13870 hp = lookupvar("HOME");
13871 if (hp) {
13872 hp = concat_path_file(hp, ".ash_history");
13873 setvar("HISTFILE", hp, 0);
13874 free((char*)hp);
13875 hp = lookupvar("HISTFILE");
13876 }
13877 }
13887 if (hp) 13878 if (hp)
13888 line_input_state->hist_file = hp; 13879 line_input_state->hist_file = hp;
13889# if ENABLE_FEATURE_SH_HISTFILESIZE 13880# if ENABLE_FEATURE_SH_HISTFILESIZE
diff --git a/shell/hush.c b/shell/hush.c
index e2dc1e2d0..b23325725 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -8880,6 +8880,9 @@ static int FAST_FUNC builtin_source(char **argv)
8880 free(arg_path); 8880 free(arg_path);
8881 if (!input) { 8881 if (!input) {
8882 /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */ 8882 /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */
8883 /* POSIX: non-interactive shell should abort here,
8884 * not merely fail. So far no one complained :)
8885 */
8883 return EXIT_FAILURE; 8886 return EXIT_FAILURE;
8884 } 8887 }
8885 close_on_exec_on(fileno(input)); 8888 close_on_exec_on(fileno(input));
@@ -8889,12 +8892,14 @@ static int FAST_FUNC builtin_source(char **argv)
8889 /* "we are inside sourced file, ok to use return" */ 8892 /* "we are inside sourced file, ok to use return" */
8890 G.flag_return_in_progress = -1; 8893 G.flag_return_in_progress = -1;
8891#endif 8894#endif
8892 save_and_replace_G_args(&sv, argv); 8895 if (argv[1])
8896 save_and_replace_G_args(&sv, argv);
8893 8897
8894 parse_and_run_file(input); 8898 parse_and_run_file(input);
8895 fclose(input); 8899 fclose(input);
8896 8900
8897 restore_G_args(&sv, argv); 8901 if (argv[1])
8902 restore_G_args(&sv, argv);
8898#if ENABLE_HUSH_FUNCTIONS 8903#if ENABLE_HUSH_FUNCTIONS
8899 G.flag_return_in_progress = sv_flg; 8904 G.flag_return_in_progress = sv_flg;
8900#endif 8905#endif
diff --git a/shell/hush_test/hush-misc/source2.right b/shell/hush_test/hush-misc/source2.right
new file mode 100644
index 000000000..0587bad67
--- /dev/null
+++ b/shell/hush_test/hush-misc/source2.right
@@ -0,0 +1,4 @@
10:arg0 1:arg1 2:arg2
2Ok1:0
30:arg0 1:q 2:w
4Ok2:0
diff --git a/shell/hush_test/hush-misc/source2.tests b/shell/hush_test/hush-misc/source2.tests
new file mode 100755
index 000000000..40b6b83cd
--- /dev/null
+++ b/shell/hush_test/hush-misc/source2.tests
@@ -0,0 +1,8 @@
1echo 'echo "0:$0 1:$1 2:$2"' >sourced1
2set -- 1 2 3
3"$THIS_SH" -c '. ./sourced1' arg0 arg1 arg2
4echo Ok1:$?
5"$THIS_SH" -c '. ./sourced1 q w e' arg0 arg1 arg2
6echo Ok2:$?
7
8rm sourced1
diff --git a/shell/math.c b/shell/math.c
index 15c003965..3da151137 100644
--- a/shell/math.c
+++ b/shell/math.c
@@ -494,18 +494,6 @@ static const char op_tokens[] ALIGN1 = {
494}; 494};
495#define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7]) 495#define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7])
496 496
497const char* FAST_FUNC
498endofname(const char *name)
499{
500 if (!is_name(*name))
501 return name;
502 while (*++name) {
503 if (!is_in_name(*name))
504 break;
505 }
506 return name;
507}
508
509static arith_t FAST_FUNC 497static arith_t FAST_FUNC
510evaluate_string(arith_state_t *math_state, const char *expr) 498evaluate_string(arith_state_t *math_state, const char *expr)
511{ 499{
diff --git a/shell/math.h b/shell/math.h
index 2d305eb12..864bee691 100644
--- a/shell/math.h
+++ b/shell/math.h
@@ -73,11 +73,6 @@ typedef long arith_t;
73#define strto_arith_t strtoul 73#define strto_arith_t strtoul
74#endif 74#endif
75 75
76/* ash's and hush's endofname is the same, so... */
77# define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
78# define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
79const char* FAST_FUNC endofname(const char *name);
80
81typedef const char* FAST_FUNC (*arith_var_lookup_t)(const char *name); 76typedef const char* FAST_FUNC (*arith_var_lookup_t)(const char *name);
82typedef void FAST_FUNC (*arith_var_set_t)(const char *name, const char *val); 77typedef void FAST_FUNC (*arith_var_set_t)(const char *name, const char *val);
83//typedef const char* FAST_FUNC (*arith_var_endofname_t)(const char *name); 78//typedef const char* FAST_FUNC (*arith_var_endofname_t)(const char *name);
diff --git a/sysklogd/syslogd.c b/sysklogd/syslogd.c
index 3fe3f5348..4f45b4f7f 100644
--- a/sysklogd/syslogd.c
+++ b/sysklogd/syslogd.c
@@ -29,7 +29,7 @@
29//usage: "\n -b N N rotated logs to keep (default:1, max=99, 0=purge)" 29//usage: "\n -b N N rotated logs to keep (default:1, max=99, 0=purge)"
30//usage: ) 30//usage: )
31//usage: IF_FEATURE_REMOTE_LOG( 31//usage: IF_FEATURE_REMOTE_LOG(
32//usage: "\n -R HOST[:PORT] Log to IP or hostname on PORT (default PORT=514/UDP)" 32//usage: "\n -R HOST[:PORT] Log to HOST:PORT (default PORT:514)"
33//usage: "\n -L Log locally and via network (default is network only if -R)" 33//usage: "\n -L Log locally and via network (default is network only if -R)"
34//usage: ) 34//usage: )
35//usage: IF_FEATURE_SYSLOGD_DUP( 35//usage: IF_FEATURE_SYSLOGD_DUP(
diff --git a/testsuite/awk.tests b/testsuite/awk.tests
index f9c3b6b4d..dad49c3f5 100755
--- a/testsuite/awk.tests
+++ b/testsuite/awk.tests
@@ -25,6 +25,25 @@ testing "awk if string == " "awk 'BEGIN{if(\"a\"==\"ab\") print \"bar\"}'" ""
25 25
26# 4294967295 = 0xffffffff 26# 4294967295 = 0xffffffff
27testing "awk bitwise op" "awk '{ print or(4294967295,1) }'" "4.29497e+09\n" "" "\n" 27testing "awk bitwise op" "awk '{ print or(4294967295,1) }'" "4.29497e+09\n" "" "\n"
28
29# we were testing for a non-empty body when deciding if a function was
30# defined or not. The testcase below caused:
31# awk: cmd. line:8: Call to undefined function
32prg='
33function empty_fun(count) {
34 # empty
35}
36END {
37 i=1
38 print "L" i "\n"
39 empty_fun(i + i + ++i)
40 print "L" i "\n"
41}'
42testing "awk handles empty function f(arg){}" \
43 "awk '$prg'" \
44 "L1\n\nL2\n\n" \
45 "" ""
46
28optional DESKTOP 47optional DESKTOP
29testing "awk hex const 1" "awk '{ print or(0xffffffff,1) }'" "4.29497e+09\n" "" "\n" 48testing "awk hex const 1" "awk '{ print or(0xffffffff,1) }'" "4.29497e+09\n" "" "\n"
30testing "awk hex const 2" "awk '{ print or(0x80000000,1) }'" "2.14748e+09\n" "" "\n" 49testing "awk hex const 2" "awk '{ print or(0x80000000,1) }'" "2.14748e+09\n" "" "\n"
diff --git a/util-linux/fdisk_sun.c b/util-linux/fdisk_sun.c
index e7fcc067c..e32740dea 100644
--- a/util-linux/fdisk_sun.c
+++ b/util-linux/fdisk_sun.c
@@ -348,6 +348,7 @@ create_sunlabel(void)
348 348
349 set_all_unchanged(); 349 set_all_unchanged();
350 set_changed(0); 350 set_changed(0);
351 check_sun_label();
351 get_boot(CREATE_EMPTY_SUN); 352 get_boot(CREATE_EMPTY_SUN);
352} 353}
353 354
@@ -497,11 +498,14 @@ add_sun_partition(int n, int sys)
497 else 498 else
498 first = read_int(scround(start), scround(stop)+1, 499 first = read_int(scround(start), scround(stop)+1,
499 scround(stop), 0, mesg); 500 scround(stop), 0, mesg);
500 if (display_in_cyl_units) 501 if (display_in_cyl_units) {
501 first *= units_per_sector; 502 first *= units_per_sector;
502 else 503 } else {
503 /* Starting sector has to be properly aligned */ 504 /* Starting sector has to be properly aligned */
504 first = (first + g_heads * g_sectors - 1) / (g_heads * g_sectors); 505 first = (first + g_heads * g_sectors - 1) /
506 (g_heads * g_sectors);
507 first *= g_heads * g_sectors;
508 }
505 if (n == 2 && first != 0) 509 if (n == 2 && first != 0)
506 printf("\ 510 printf("\
507It is highly recommended that the third partition covers the whole disk\n\ 511It is highly recommended that the third partition covers the whole disk\n\
diff --git a/util-linux/losetup.c b/util-linux/losetup.c
index 21108d0bf..c69763335 100644
--- a/util-linux/losetup.c
+++ b/util-linux/losetup.c
@@ -8,16 +8,16 @@
8 */ 8 */
9 9
10//usage:#define losetup_trivial_usage 10//usage:#define losetup_trivial_usage
11//usage: "[-r] [-o OFS] LOOPDEV FILE - associate loop devices\n" 11//usage: "[-r] [-o OFS] {-f|LOOPDEV} FILE - associate loop devices\n"
12//usage: " losetup -d LOOPDEV - disassociate\n" 12//usage: " losetup -d LOOPDEV - disassociate\n"
13//usage: " losetup [-f] - show" 13//usage: " losetup -a - show status of all\n"
14//usage: " losetup -f - show next available"
14//usage:#define losetup_full_usage "\n\n" 15//usage:#define losetup_full_usage "\n\n"
15//usage: " -o OFS Start OFS bytes into FILE" 16//usage: " -o OFS Start OFS bytes into FILE"
16//usage: "\n -r Read-only" 17//usage: "\n -r Read-only"
17//usage: "\n -f Show first free loop device" 18//usage: "\n -f Show/find first free loop device"
18//usage: 19//usage:
19//usage:#define losetup_notes_usage 20//usage:#define losetup_notes_usage
20//usage: "No arguments will display all current associations.\n"
21//usage: "One argument (losetup /dev/loop1) will display the current association\n" 21//usage: "One argument (losetup /dev/loop1) will display the current association\n"
22//usage: "(if any), or disassociate it (with -d). The display shows the offset\n" 22//usage: "(if any), or disassociate it (with -d). The display shows the offset\n"
23//usage: "and filename of the file the loop device is currently bound to.\n\n" 23//usage: "and filename of the file the loop device is currently bound to.\n\n"
@@ -31,77 +31,91 @@ int losetup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
31int losetup_main(int argc UNUSED_PARAM, char **argv) 31int losetup_main(int argc UNUSED_PARAM, char **argv)
32{ 32{
33 unsigned opt; 33 unsigned opt;
34 int n;
35 char *opt_o; 34 char *opt_o;
36 unsigned long long offset = 0; 35 char dev[LOOP_NAMESIZE];
37 enum { 36 enum {
38 OPT_d = (1 << 0), 37 OPT_d = (1 << 0),
39 OPT_o = (1 << 1), 38 OPT_o = (1 << 1),
40 OPT_f = (1 << 2), 39 OPT_f = (1 << 2),
41 OPT_r = (1 << 3), /* must be last */ 40 OPT_a = (1 << 3),
41 OPT_r = (1 << 4), /* must be last */
42 }; 42 };
43 43
44 /* max 2 args, -d,-o,-f opts are mutually exclusive */ 44 opt_complementary = "?2:d--ofar:a--ofr";
45 opt_complementary = "?2:d--of:o--df:f--do"; 45 opt = getopt32(argv, "do:far", &opt_o);
46 opt = getopt32(argv, "do:fr", &opt_o);
47 argv += optind; 46 argv += optind;
48 47
49 if (opt == OPT_o) 48 /* LOOPDEV */
50 offset = xatoull(opt_o); 49 if (!opt && argv[0] && !argv[1]) {
50 char *s;
51 51
52 s = query_loop(argv[0]);
53 if (!s)
54 bb_simple_perror_msg_and_die(argv[0]);
55 printf("%s: %s\n", argv[0], s);
56 if (ENABLE_FEATURE_CLEAN_UP)
57 free(s);
58 return EXIT_SUCCESS;
59 }
60
61 /* -d LOOPDEV */
52 if (opt == OPT_d) { 62 if (opt == OPT_d) {
53 /* -d BLOCKDEV */
54 if (!argv[0] || argv[1])
55 bb_show_usage();
56 if (del_loop(argv[0])) 63 if (del_loop(argv[0]))
57 bb_simple_perror_msg_and_die(argv[0]); 64 bb_simple_perror_msg_and_die(argv[0]);
58 return EXIT_SUCCESS; 65 return EXIT_SUCCESS;
59 } 66 }
60 67
61 if (argv[0]) { 68 /* -a */
62 char *s; 69 if (opt == OPT_a) {
63 70 int n;
64 if (opt == OPT_f) /* -f should not have arguments */ 71 for (n = 0; n < 10; n++) {
65 bb_show_usage(); 72 char *s;
66 73
67 if (argv[1]) { 74 sprintf(dev, LOOP_FORMAT, n);
68 /* [-r] [-o OFS] BLOCKDEV FILE */ 75 s = query_loop(dev);
69 if (set_loop(&argv[0], argv[1], offset, (opt / OPT_r)) < 0) 76 if (s) {
70 bb_simple_perror_msg_and_die(argv[0]); 77 printf("%s: %s\n", dev, s);
71 return EXIT_SUCCESS; 78 if (ENABLE_FEATURE_CLEAN_UP)
79 free(s);
80 }
72 } 81 }
73 /* [-r] [-o OFS] BLOCKDEV */
74 s = query_loop(argv[0]);
75 if (!s)
76 bb_simple_perror_msg_and_die(argv[0]);
77 printf("%s: %s\n", argv[0], s);
78 if (ENABLE_FEATURE_CLEAN_UP)
79 free(s);
80 return EXIT_SUCCESS; 82 return EXIT_SUCCESS;
81 } 83 }
82 84
83 /* [-r] [-o OFS|-f] with no params */ 85 /* contains -f */
84 n = 0; 86 if (opt & OPT_f) {
85 while (1) {
86 char *s; 87 char *s;
87 char dev[LOOP_NAMESIZE]; 88 int n = 0;
88 89
89 sprintf(dev, LOOP_FORMAT, n); 90 do {
90 s = query_loop(dev); 91 sprintf(dev, LOOP_FORMAT, n);
91 n++; 92 s = query_loop(dev);
92 if (!s) { 93 if (s && ENABLE_FEATURE_CLEAN_UP)
93 if (n > 9 && errno && errno != ENXIO)
94 return EXIT_SUCCESS;
95 if (opt == OPT_f) {
96 puts(dev);
97 return EXIT_SUCCESS;
98 }
99 } else {
100 if (opt != OPT_f)
101 printf("%s: %s\n", dev, s);
102 if (ENABLE_FEATURE_CLEAN_UP)
103 free(s); 94 free(s);
95 } while (s);
96 if ((opt == OPT_f) && !argv[0]) {
97 puts(dev);
98 return EXIT_SUCCESS;
104 } 99 }
105 } 100 }
106 return EXIT_SUCCESS; 101
102 /* [-r] [-o OFS] {-f|LOOPDEV} FILE */
103 if (argv[0] && ((opt & OPT_f) || argv[1])) {
104 unsigned long long offset = 0;
105 char *d = dev;
106
107 if (opt == OPT_o)
108 offset = xatoull(opt_o);
109 if (opt != OPT_f)
110 d = *(argv++);
111
112 if (argv[0]) {
113 if (set_loop(&d, argv[0], offset, (opt / OPT_r)) < 0)
114 bb_simple_perror_msg_and_die(argv[0]);
115 return EXIT_SUCCESS;
116 }
117 }
118
119 bb_show_usage();
120 return EXIT_FAILURE;
107} 121}
diff --git a/util-linux/mdev.c b/util-linux/mdev.c
index c592ef687..5fe6bbbde 100644
--- a/util-linux/mdev.c
+++ b/util-linux/mdev.c
@@ -80,7 +80,7 @@
80//usage: IF_FEATURE_MDEV_CONF( 80//usage: IF_FEATURE_MDEV_CONF(
81//usage: "\n" 81//usage: "\n"
82//usage: "It uses /etc/mdev.conf with lines\n" 82//usage: "It uses /etc/mdev.conf with lines\n"
83//usage: " [-]DEVNAME UID:GID PERM" 83//usage: " [-][ENV=regex;]...DEVNAME UID:GID PERM"
84//usage: IF_FEATURE_MDEV_RENAME(" [>|=PATH]|[!]") 84//usage: IF_FEATURE_MDEV_RENAME(" [>|=PATH]|[!]")
85//usage: IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]") 85//usage: IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]")
86//usage: "\n" 86//usage: "\n"
@@ -230,9 +230,34 @@
230 * SUBSYSTEM=block 230 * SUBSYSTEM=block
231 */ 231 */
232 232
233static const char keywords[] ALIGN1 = "add\0remove\0change\0"; 233#define DEBUG_LVL 2
234
235#if DEBUG_LVL >= 1
236# define dbg1(...) do { if (G.verbose) bb_error_msg(__VA_ARGS__); } while(0)
237#else
238# define dbg1(...) ((void)0)
239#endif
240#if DEBUG_LVL >= 2
241# define dbg2(...) do { if (G.verbose >= 2) bb_error_msg(__VA_ARGS__); } while(0)
242#else
243# define dbg2(...) ((void)0)
244#endif
245#if DEBUG_LVL >= 3
246# define dbg3(...) do { if (G.verbose >= 3) bb_error_msg(__VA_ARGS__); } while(0)
247#else
248# define dbg3(...) ((void)0)
249#endif
250
251
252static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0"
234enum { OP_add, OP_remove }; 253enum { OP_add, OP_remove };
235 254
255struct envmatch {
256 struct envmatch *next;
257 char *envname;
258 regex_t match;
259};
260
236struct rule { 261struct rule {
237 bool keep_matching; 262 bool keep_matching;
238 bool regex_compiled; 263 bool regex_compiled;
@@ -243,12 +268,14 @@ struct rule {
243 char *ren_mov; 268 char *ren_mov;
244 IF_FEATURE_MDEV_EXEC(char *r_cmd;) 269 IF_FEATURE_MDEV_EXEC(char *r_cmd;)
245 regex_t match; 270 regex_t match;
271 struct envmatch *envmatch;
246}; 272};
247 273
248struct globals { 274struct globals {
249 int root_major, root_minor; 275 int root_major, root_minor;
250 smallint verbose; 276 smallint verbose;
251 char *subsystem; 277 char *subsystem;
278 char *subsys_env; /* for putenv("SUBSYSTEM=subsystem") */
252#if ENABLE_FEATURE_MDEV_CONF 279#if ENABLE_FEATURE_MDEV_CONF
253 const char *filename; 280 const char *filename;
254 parser_t *parser; 281 parser_t *parser;
@@ -256,6 +283,7 @@ struct globals {
256 unsigned rule_idx; 283 unsigned rule_idx;
257#endif 284#endif
258 struct rule cur_rule; 285 struct rule cur_rule;
286 char timestr[sizeof("60.123456")];
259} FIX_ALIASING; 287} FIX_ALIASING;
260#define G (*(struct globals*)&bb_common_bufsiz1) 288#define G (*(struct globals*)&bb_common_bufsiz1)
261#define INIT_G() do { \ 289#define INIT_G() do { \
@@ -270,13 +298,6 @@ struct globals {
270/* We use additional 64+ bytes in make_device() */ 298/* We use additional 64+ bytes in make_device() */
271#define SCRATCH_SIZE 80 299#define SCRATCH_SIZE 80
272 300
273#if 0
274# define dbg(...) bb_error_msg(__VA_ARGS__)
275#else
276# define dbg(...) ((void)0)
277#endif
278
279
280#if ENABLE_FEATURE_MDEV_CONF 301#if ENABLE_FEATURE_MDEV_CONF
281 302
282static void make_default_cur_rule(void) 303static void make_default_cur_rule(void)
@@ -288,14 +309,48 @@ static void make_default_cur_rule(void)
288 309
289static void clean_up_cur_rule(void) 310static void clean_up_cur_rule(void)
290{ 311{
312 struct envmatch *e;
313
291 free(G.cur_rule.envvar); 314 free(G.cur_rule.envvar);
315 free(G.cur_rule.ren_mov);
292 if (G.cur_rule.regex_compiled) 316 if (G.cur_rule.regex_compiled)
293 regfree(&G.cur_rule.match); 317 regfree(&G.cur_rule.match);
294 free(G.cur_rule.ren_mov);
295 IF_FEATURE_MDEV_EXEC(free(G.cur_rule.r_cmd);) 318 IF_FEATURE_MDEV_EXEC(free(G.cur_rule.r_cmd);)
319 e = G.cur_rule.envmatch;
320 while (e) {
321 free(e->envname);
322 regfree(&e->match);
323 e = e->next;
324 }
296 make_default_cur_rule(); 325 make_default_cur_rule();
297} 326}
298 327
328static char *parse_envmatch_pfx(char *val)
329{
330 struct envmatch **nextp = &G.cur_rule.envmatch;
331
332 for (;;) {
333 struct envmatch *e;
334 char *semicolon;
335 char *eq = strchr(val, '=');
336 if (!eq /* || eq == val? */)
337 return val;
338 if (endofname(val) != eq)
339 return val;
340 semicolon = strchr(eq, ';');
341 if (!semicolon)
342 return val;
343 /* ENVVAR=regex;... */
344 *nextp = e = xzalloc(sizeof(*e));
345 nextp = &e->next;
346 e->envname = xstrndup(val, eq - val);
347 *semicolon = '\0';
348 xregcomp(&e->match, eq + 1, REG_EXTENDED);
349 *semicolon = ';';
350 val = semicolon + 1;
351 }
352}
353
299static void parse_next_rule(void) 354static void parse_next_rule(void)
300{ 355{
301 /* Note: on entry, G.cur_rule is set to default */ 356 /* Note: on entry, G.cur_rule is set to default */
@@ -308,12 +363,13 @@ static void parse_next_rule(void)
308 break; 363 break;
309 364
310 /* Fields: [-]regex uid:gid mode [alias] [cmd] */ 365 /* Fields: [-]regex uid:gid mode [alias] [cmd] */
311 dbg("token1:'%s'", tokens[1]); 366 dbg3("token1:'%s'", tokens[1]);
312 367
313 /* 1st field */ 368 /* 1st field */
314 val = tokens[0]; 369 val = tokens[0];
315 G.cur_rule.keep_matching = ('-' == val[0]); 370 G.cur_rule.keep_matching = ('-' == val[0]);
316 val += G.cur_rule.keep_matching; /* swallow leading dash */ 371 val += G.cur_rule.keep_matching; /* swallow leading dash */
372 val = parse_envmatch_pfx(val);
317 if (val[0] == '@') { 373 if (val[0] == '@') {
318 /* @major,minor[-minor2] */ 374 /* @major,minor[-minor2] */
319 /* (useful when name is ambiguous: 375 /* (useful when name is ambiguous:
@@ -328,8 +384,10 @@ static void parse_next_rule(void)
328 if (sc == 2) 384 if (sc == 2)
329 G.cur_rule.min1 = G.cur_rule.min0; 385 G.cur_rule.min1 = G.cur_rule.min0;
330 } else { 386 } else {
387 char *eq = strchr(val, '=');
331 if (val[0] == '$') { 388 if (val[0] == '$') {
332 char *eq = strchr(++val, '='); 389 /* $ENVVAR=regex ... */
390 val++;
333 if (!eq) { 391 if (!eq) {
334 bb_error_msg("bad $envvar=regex on line %d", G.parser->lineno); 392 bb_error_msg("bad $envvar=regex on line %d", G.parser->lineno);
335 goto next_rule; 393 goto next_rule;
@@ -373,7 +431,7 @@ static void parse_next_rule(void)
373 clean_up_cur_rule(); 431 clean_up_cur_rule();
374 } /* while (config_read) */ 432 } /* while (config_read) */
375 433
376 dbg("config_close(G.parser)"); 434 dbg3("config_close(G.parser)");
377 config_close(G.parser); 435 config_close(G.parser);
378 G.parser = NULL; 436 G.parser = NULL;
379 437
@@ -390,7 +448,7 @@ static const struct rule *next_rule(void)
390 448
391 /* Open conf file if we didn't do it yet */ 449 /* Open conf file if we didn't do it yet */
392 if (!G.parser && G.filename) { 450 if (!G.parser && G.filename) {
393 dbg("config_open('%s')", G.filename); 451 dbg3("config_open('%s')", G.filename);
394 G.parser = config_open2(G.filename, fopen_for_read); 452 G.parser = config_open2(G.filename, fopen_for_read);
395 G.filename = NULL; 453 G.filename = NULL;
396 } 454 }
@@ -399,7 +457,7 @@ static const struct rule *next_rule(void)
399 /* mdev -s */ 457 /* mdev -s */
400 /* Do we have rule parsed already? */ 458 /* Do we have rule parsed already? */
401 if (G.rule_vec[G.rule_idx]) { 459 if (G.rule_vec[G.rule_idx]) {
402 dbg("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]); 460 dbg3("< G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
403 return G.rule_vec[G.rule_idx++]; 461 return G.rule_vec[G.rule_idx++];
404 } 462 }
405 make_default_cur_rule(); 463 make_default_cur_rule();
@@ -416,13 +474,28 @@ static const struct rule *next_rule(void)
416 rule = memcpy(xmalloc(sizeof(G.cur_rule)), &G.cur_rule, sizeof(G.cur_rule)); 474 rule = memcpy(xmalloc(sizeof(G.cur_rule)), &G.cur_rule, sizeof(G.cur_rule));
417 G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx); 475 G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx);
418 G.rule_vec[G.rule_idx++] = rule; 476 G.rule_vec[G.rule_idx++] = rule;
419 dbg("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]); 477 dbg3("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
420 } 478 }
421 } 479 }
422 480
423 return rule; 481 return rule;
424} 482}
425 483
484static int env_matches(struct envmatch *e)
485{
486 while (e) {
487 int r;
488 char *val = getenv(e->envname);
489 if (!val)
490 return 0;
491 r = regexec(&e->match, val, /*size*/ 0, /*range[]*/ NULL, /*eflags*/ 0);
492 if (r != 0) /* no match */
493 return 0;
494 e = e->next;
495 }
496 return 1;
497}
498
426#else 499#else
427 500
428# define next_rule() (&G.cur_rule) 501# define next_rule() (&G.cur_rule)
@@ -479,9 +552,6 @@ static void make_device(char *device_name, char *path, int operation)
479{ 552{
480 int major, minor, type, len; 553 int major, minor, type, len;
481 554
482 if (G.verbose)
483 bb_error_msg("device: %s, %s", device_name, path);
484
485 /* Try to read major/minor string. Note that the kernel puts \n after 555 /* Try to read major/minor string. Note that the kernel puts \n after
486 * the data, so we don't need to worry about null terminating the string 556 * the data, so we don't need to worry about null terminating the string
487 * because sscanf() will stop at the first nondigit, which \n is. 557 * because sscanf() will stop at the first nondigit, which \n is.
@@ -500,8 +570,7 @@ static void make_device(char *device_name, char *path, int operation)
500 /* no "dev" file, but we can still run scripts 570 /* no "dev" file, but we can still run scripts
501 * based on device name */ 571 * based on device name */
502 } else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) == 2) { 572 } else if (sscanf(++dev_maj_min, "%u:%u", &major, &minor) == 2) {
503 if (G.verbose) 573 dbg1("dev %u,%u", major, minor);
504 bb_error_msg("maj,min: %u,%u", major, minor);
505 } else { 574 } else {
506 major = -1; 575 major = -1;
507 } 576 }
@@ -511,7 +580,8 @@ static void make_device(char *device_name, char *path, int operation)
511 /* Determine device name, type, major and minor */ 580 /* Determine device name, type, major and minor */
512 if (!device_name) 581 if (!device_name)
513 device_name = (char*) bb_basename(path); 582 device_name = (char*) bb_basename(path);
514 /* http://kernel.org/doc/pending/hotplug.txt says that only 583 /*
584 * http://kernel.org/doc/pending/hotplug.txt says that only
515 * "/sys/block/..." is for block devices. "/sys/bus" etc is not. 585 * "/sys/block/..." is for block devices. "/sys/bus" etc is not.
516 * But since 2.6.25 block devices are also in /sys/class/block. 586 * But since 2.6.25 block devices are also in /sys/class/block.
517 * We use strstr("/block/") to forestall future surprises. 587 * We use strstr("/block/") to forestall future surprises.
@@ -537,6 +607,8 @@ static void make_device(char *device_name, char *path, int operation)
537 rule = next_rule(); 607 rule = next_rule();
538 608
539#if ENABLE_FEATURE_MDEV_CONF 609#if ENABLE_FEATURE_MDEV_CONF
610 if (!env_matches(rule->envmatch))
611 continue;
540 if (rule->maj >= 0) { /* @maj,min rule */ 612 if (rule->maj >= 0) { /* @maj,min rule */
541 if (major != rule->maj) 613 if (major != rule->maj)
542 continue; 614 continue;
@@ -547,7 +619,7 @@ static void make_device(char *device_name, char *path, int operation)
547 } 619 }
548 if (rule->envvar) { /* $envvar=regex rule */ 620 if (rule->envvar) { /* $envvar=regex rule */
549 str_to_match = getenv(rule->envvar); 621 str_to_match = getenv(rule->envvar);
550 dbg("getenv('%s'):'%s'", rule->envvar, str_to_match); 622 dbg3("getenv('%s'):'%s'", rule->envvar, str_to_match);
551 if (!str_to_match) 623 if (!str_to_match)
552 continue; 624 continue;
553 } 625 }
@@ -555,7 +627,7 @@ static void make_device(char *device_name, char *path, int operation)
555 627
556 if (rule->regex_compiled) { 628 if (rule->regex_compiled) {
557 int regex_match = regexec(&rule->match, str_to_match, ARRAY_SIZE(off), off, 0); 629 int regex_match = regexec(&rule->match, str_to_match, ARRAY_SIZE(off), off, 0);
558 dbg("regex_match for '%s':%d", str_to_match, regex_match); 630 dbg3("regex_match for '%s':%d", str_to_match, regex_match);
559 //bb_error_msg("matches:"); 631 //bb_error_msg("matches:");
560 //for (int i = 0; i < ARRAY_SIZE(off); i++) { 632 //for (int i = 0; i < ARRAY_SIZE(off); i++) {
561 // if (off[i].rm_so < 0) continue; 633 // if (off[i].rm_so < 0) continue;
@@ -574,9 +646,8 @@ static void make_device(char *device_name, char *path, int operation)
574 } 646 }
575 /* else: it's final implicit "match-all" rule */ 647 /* else: it's final implicit "match-all" rule */
576 rule_matches: 648 rule_matches:
649 dbg2("rule matched, line %d", G.parser ? G.parser->lineno : -1);
577#endif 650#endif
578 dbg("rule matched");
579
580 /* Build alias name */ 651 /* Build alias name */
581 alias = NULL; 652 alias = NULL;
582 if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) { 653 if (ENABLE_FEATURE_MDEV_RENAME && rule->ren_mov) {
@@ -619,34 +690,30 @@ static void make_device(char *device_name, char *path, int operation)
619 } 690 }
620 } 691 }
621 } 692 }
622 dbg("alias:'%s'", alias); 693 dbg3("alias:'%s'", alias);
623 694
624 command = NULL; 695 command = NULL;
625 IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;) 696 IF_FEATURE_MDEV_EXEC(command = rule->r_cmd;)
626 if (command) { 697 if (command) {
627 const char *s = "$@*";
628 const char *s2 = strchr(s, command[0]);
629
630 /* Are we running this command now? 698 /* Are we running this command now?
631 * Run $cmd on delete, @cmd on create, *cmd on both 699 * Run @cmd on create, $cmd on delete, *cmd on any
632 */ 700 */
633 if (s2 - s != (operation == OP_remove) || *s2 == '*') { 701 if ((command[0] == '@' && operation == OP_add)
634 /* We are here if: '*', 702 || (command[0] == '$' && operation == OP_remove)
635 * or: '@' and delete = 0, 703 || (command[0] == '*')
636 * or: '$' and delete = 1 704 ) {
637 */
638 command++; 705 command++;
639 } else { 706 } else {
640 command = NULL; 707 command = NULL;
641 } 708 }
642 } 709 }
643 dbg("command:'%s'", command); 710 dbg3("command:'%s'", command);
644 711
645 /* "Execute" the line we found */ 712 /* "Execute" the line we found */
646 node_name = device_name; 713 node_name = device_name;
647 if (ENABLE_FEATURE_MDEV_RENAME && alias) { 714 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
648 node_name = alias = build_alias(alias, device_name); 715 node_name = alias = build_alias(alias, device_name);
649 dbg("alias2:'%s'", alias); 716 dbg3("alias2:'%s'", alias);
650 } 717 }
651 718
652 if (operation == OP_add && major >= 0) { 719 if (operation == OP_add && major >= 0) {
@@ -656,13 +723,20 @@ static void make_device(char *device_name, char *path, int operation)
656 mkdir_recursive(node_name); 723 mkdir_recursive(node_name);
657 *slash = '/'; 724 *slash = '/';
658 } 725 }
659 if (G.verbose) 726 if (ENABLE_FEATURE_MDEV_CONF) {
660 bb_error_msg("mknod: %s (%d,%d) %o", node_name, major, minor, rule->mode | type); 727 dbg1("mknod %s (%d,%d) %o"
728 " %u:%u",
729 node_name, major, minor, rule->mode | type,
730 rule->ugid.uid, rule->ugid.gid
731 );
732 } else {
733 dbg1("mknod %s (%d,%d) %o",
734 node_name, major, minor, rule->mode | type
735 );
736 }
661 if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST) 737 if (mknod(node_name, rule->mode | type, makedev(major, minor)) && errno != EEXIST)
662 bb_perror_msg("can't create '%s'", node_name); 738 bb_perror_msg("can't create '%s'", node_name);
663 if (ENABLE_FEATURE_MDEV_CONF) { 739 if (ENABLE_FEATURE_MDEV_CONF) {
664 if (G.verbose)
665 bb_error_msg("chmod: %o chown: %u:%u", rule->mode, rule->ugid.uid, rule->ugid.gid);
666 chmod(node_name, rule->mode); 740 chmod(node_name, rule->mode);
667 chown(node_name, rule->ugid.uid, rule->ugid.gid); 741 chown(node_name, rule->ugid.uid, rule->ugid.gid);
668 } 742 }
@@ -673,8 +747,7 @@ static void make_device(char *device_name, char *path, int operation)
673//TODO: on devtmpfs, device_name already exists and symlink() fails. 747//TODO: on devtmpfs, device_name already exists and symlink() fails.
674//End result is that instead of symlink, we have two nodes. 748//End result is that instead of symlink, we have two nodes.
675//What should be done? 749//What should be done?
676 if (G.verbose) 750 dbg1("symlink: %s", device_name);
677 bb_error_msg("symlink: %s", device_name);
678 symlink(node_name, device_name); 751 symlink(node_name, device_name);
679 } 752 }
680 } 753 }
@@ -683,27 +756,21 @@ static void make_device(char *device_name, char *path, int operation)
683 if (ENABLE_FEATURE_MDEV_EXEC && command) { 756 if (ENABLE_FEATURE_MDEV_EXEC && command) {
684 /* setenv will leak memory, use putenv/unsetenv/free */ 757 /* setenv will leak memory, use putenv/unsetenv/free */
685 char *s = xasprintf("%s=%s", "MDEV", node_name); 758 char *s = xasprintf("%s=%s", "MDEV", node_name);
686 char *s1 = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
687 putenv(s); 759 putenv(s);
688 putenv(s1); 760 dbg1("running: %s", command);
689 if (G.verbose)
690 bb_error_msg("running: %s", command);
691 if (system(command) == -1) 761 if (system(command) == -1)
692 bb_perror_msg("can't run '%s'", command); 762 bb_perror_msg("can't run '%s'", command);
693 bb_unsetenv_and_free(s1);
694 bb_unsetenv_and_free(s); 763 bb_unsetenv_and_free(s);
695 } 764 }
696 765
697 if (operation == OP_remove && major >= -1) { 766 if (operation == OP_remove && major >= -1) {
698 if (ENABLE_FEATURE_MDEV_RENAME && alias) { 767 if (ENABLE_FEATURE_MDEV_RENAME && alias) {
699 if (aliaslink == '>') { 768 if (aliaslink == '>') {
700 if (G.verbose) 769 dbg1("unlink: %s", device_name);
701 bb_error_msg("unlink: %s", device_name);
702 unlink(device_name); 770 unlink(device_name);
703 } 771 }
704 } 772 }
705 if (G.verbose) 773 dbg1("unlink: %s", node_name);
706 bb_error_msg("unlink: %s", node_name);
707 unlink(node_name); 774 unlink(node_name);
708 } 775 }
709 776
@@ -748,9 +815,16 @@ static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
748 * under /sys/class/ */ 815 * under /sys/class/ */
749 if (1 == depth) { 816 if (1 == depth) {
750 free(G.subsystem); 817 free(G.subsystem);
818 if (G.subsys_env) {
819 bb_unsetenv_and_free(G.subsys_env);
820 G.subsys_env = NULL;
821 }
751 G.subsystem = strrchr(fileName, '/'); 822 G.subsystem = strrchr(fileName, '/');
752 if (G.subsystem) 823 if (G.subsystem) {
753 G.subsystem = xstrdup(G.subsystem + 1); 824 G.subsystem = xstrdup(G.subsystem + 1);
825 G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
826 putenv(G.subsys_env);
827 }
754 } 828 }
755 829
756 return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE); 830 return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
@@ -822,6 +896,100 @@ static void load_firmware(const char *firmware, const char *sysfs_path)
822 } 896 }
823} 897}
824 898
899static char *curtime(void)
900{
901 struct timeval tv;
902 gettimeofday(&tv, NULL);
903 sprintf(G.timestr, "%u.%06u", (unsigned)tv.tv_sec % 60, (unsigned)tv.tv_usec);
904 return G.timestr;
905}
906
907static void open_mdev_log(const char *seq, unsigned my_pid)
908{
909 int logfd = open("mdev.log", O_WRONLY | O_APPEND);
910 if (logfd >= 0) {
911 xmove_fd(logfd, STDERR_FILENO);
912 G.verbose = 2;
913 applet_name = xasprintf("%s[%s]", applet_name, seq ? seq : utoa(my_pid));
914 }
915}
916
917/* If it exists, does /dev/mdev.seq match $SEQNUM?
918 * If it does not match, earlier mdev is running
919 * in parallel, and we need to wait.
920 * Active mdev pokes us with SIGCHLD to check the new file.
921 */
922static int
923wait_for_seqfile(const char *seq)
924{
925 /* We time out after 2 sec */
926 static const struct timespec ts = { 0, 32*1000*1000 };
927 int timeout = 2000 / 32;
928 int seq_fd = -1;
929 int do_once = 1;
930 sigset_t set_CHLD;
931
932 sigemptyset(&set_CHLD);
933 sigaddset(&set_CHLD, SIGCHLD);
934 sigprocmask(SIG_BLOCK, &set_CHLD, NULL);
935
936 for (;;) {
937 int seqlen;
938 char seqbuf[sizeof(int)*3 + 2];
939
940 if (seq_fd < 0) {
941 seq_fd = open("mdev.seq", O_RDWR);
942 if (seq_fd < 0)
943 break;
944 }
945 seqlen = pread(seq_fd, seqbuf, sizeof(seqbuf) - 1, 0);
946 if (seqlen < 0) {
947 close(seq_fd);
948 seq_fd = -1;
949 break;
950 }
951 seqbuf[seqlen] = '\0';
952 if (seqbuf[0] == '\n') {
953 /* seed file: write out seq ASAP */
954 xwrite_str(seq_fd, seq);
955 xlseek(seq_fd, 0, SEEK_SET);
956 dbg2("first seq written");
957 break;
958 }
959 if (strcmp(seq, seqbuf) == 0) {
960 /* correct idx */
961 break;
962 }
963 if (do_once) {
964 dbg2("%s waiting for '%s'", curtime(), seqbuf);
965 do_once = 0;
966 }
967 if (sigtimedwait(&set_CHLD, NULL, &ts) >= 0) {
968 dbg3("woken up");
969 continue; /* don't decrement timeout! */
970 }
971 if (--timeout == 0) {
972 dbg1("%s waiting for '%s'", "timed out", seqbuf);
973 break;
974 }
975 }
976 sigprocmask(SIG_UNBLOCK, &set_CHLD, NULL);
977 return seq_fd;
978}
979
980static void signal_mdevs(unsigned my_pid)
981{
982 procps_status_t* p = NULL;
983 while ((p = procps_scan(p, PSSCAN_ARGV0)) != NULL) {
984 if (p->pid != my_pid
985 && p->argv0
986 && strcmp(bb_basename(p->argv0), "mdev") == 0
987 ) {
988 kill(p->pid, SIGCHLD);
989 }
990 }
991}
992
825int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 993int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
826int mdev_main(int argc UNUSED_PARAM, char **argv) 994int mdev_main(int argc UNUSED_PARAM, char **argv)
827{ 995{
@@ -843,8 +1011,8 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
843 xchdir("/dev"); 1011 xchdir("/dev");
844 1012
845 if (argv[1] && strcmp(argv[1], "-s") == 0) { 1013 if (argv[1] && strcmp(argv[1], "-s") == 0) {
846 /* Scan: 1014 /*
847 * mdev -s 1015 * Scan: mdev -s
848 */ 1016 */
849 struct stat st; 1017 struct stat st;
850 1018
@@ -856,6 +1024,8 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
856 G.root_major = major(st.st_dev); 1024 G.root_major = major(st.st_dev);
857 G.root_minor = minor(st.st_dev); 1025 G.root_minor = minor(st.st_dev);
858 1026
1027 putenv((char*)"ACTION=add");
1028
859 /* ACTION_FOLLOWLINKS is needed since in newer kernels 1029 /* ACTION_FOLLOWLINKS is needed since in newer kernels
860 * /sys/block/loop* (for example) are symlinks to dirs, 1030 * /sys/block/loop* (for example) are symlinks to dirs,
861 * not real directories. 1031 * not real directories.
@@ -881,11 +1051,13 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
881 char *action; 1051 char *action;
882 char *env_devname; 1052 char *env_devname;
883 char *env_devpath; 1053 char *env_devpath;
1054 unsigned my_pid;
1055 int seq_fd;
884 smalluint op; 1056 smalluint op;
885 1057
886 /* Hotplug: 1058 /* Hotplug:
887 * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev 1059 * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev
888 * ACTION can be "add" or "remove" 1060 * ACTION can be "add", "remove", "change"
889 * DEVPATH is like "/block/sda" or "/class/input/mice" 1061 * DEVPATH is like "/block/sda" or "/class/input/mice"
890 */ 1062 */
891 action = getenv("ACTION"); 1063 action = getenv("ACTION");
@@ -896,41 +1068,20 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
896 if (!action || !env_devpath /*|| !G.subsystem*/) 1068 if (!action || !env_devpath /*|| !G.subsystem*/)
897 bb_show_usage(); 1069 bb_show_usage();
898 fw = getenv("FIRMWARE"); 1070 fw = getenv("FIRMWARE");
899 /* If it exists, does /dev/mdev.seq match $SEQNUM?
900 * If it does not match, earlier mdev is running
901 * in parallel, and we need to wait */
902 seq = getenv("SEQNUM"); 1071 seq = getenv("SEQNUM");
903 if (seq) {
904 int timeout = 2000 / 32; /* 2000 msec */
905 do {
906 int seqlen;
907 char seqbuf[sizeof(int)*3 + 2];
908
909 seqlen = open_read_close("mdev.seq", seqbuf, sizeof(seqbuf) - 1);
910 if (seqlen < 0) {
911 seq = NULL;
912 break;
913 }
914 seqbuf[seqlen] = '\0';
915 if (seqbuf[0] == '\n' /* seed file? */
916 || strcmp(seq, seqbuf) == 0 /* correct idx? */
917 ) {
918 break;
919 }
920 usleep(32*1000);
921 } while (--timeout);
922 }
923 1072
924 { 1073 my_pid = getpid();
925 int logfd = open("mdev.log", O_WRONLY | O_APPEND); 1074 open_mdev_log(seq, my_pid);
926 if (logfd >= 0) { 1075
927 xmove_fd(logfd, STDERR_FILENO); 1076 seq_fd = seq ? wait_for_seqfile(seq) : -1;
928 G.verbose = 1; 1077
929 if (seq) 1078 dbg1("%s "
930 applet_name = xasprintf("%s[%s]", applet_name, seq); 1079 "ACTION:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s"
931 bb_error_msg("action: %s", action); 1080 "%s%s",
932 } 1081 curtime(),
933 } 1082 action, G.subsystem, env_devname, env_devpath,
1083 fw ? " FW:" : "", fw ? fw : ""
1084 );
934 1085
935 snprintf(temp, PATH_MAX, "/sys%s", env_devpath); 1086 snprintf(temp, PATH_MAX, "/sys%s", env_devpath);
936 if (op == OP_remove) { 1087 if (op == OP_remove) {
@@ -940,16 +1091,18 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
940 if (!fw) 1091 if (!fw)
941 make_device(env_devname, temp, op); 1092 make_device(env_devname, temp, op);
942 } 1093 }
943 else if (op == OP_add) { 1094 else {
944 make_device(env_devname, temp, op); 1095 make_device(env_devname, temp, op);
945 if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) { 1096 if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
946 if (fw) 1097 if (op == OP_add && fw)
947 load_firmware(fw, temp); 1098 load_firmware(fw, temp);
948 } 1099 }
949 } 1100 }
950 1101
951 if (seq) { 1102 dbg1("%s exiting", curtime());
952 xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1)); 1103 if (seq_fd >= 0) {
1104 xwrite_str(seq_fd, utoa(xatou(seq) + 1));
1105 signal_mdevs(my_pid);
953 } 1106 }
954 } 1107 }
955 1108
diff --git a/util-linux/mkfs_minix.c b/util-linux/mkfs_minix.c
index 59d7d23d4..49afd1176 100644
--- a/util-linux/mkfs_minix.c
+++ b/util-linux/mkfs_minix.c
@@ -196,54 +196,6 @@ static void minix_clrbit(char *a, unsigned i)
196# define BLKGETSIZE _IO(0x12,96) /* return device size */ 196# define BLKGETSIZE _IO(0x12,96) /* return device size */
197#endif 197#endif
198 198
199
200static long valid_offset(int fd, int offset)
201{
202 char ch;
203
204 if (lseek(fd, offset, SEEK_SET) < 0)
205 return 0;
206 if (read(fd, &ch, 1) < 1)
207 return 0;
208 return 1;
209}
210
211static int count_blocks(int fd)
212{
213 int high, low;
214
215 low = 0;
216 for (high = 1; valid_offset(fd, high); high *= 2)
217 low = high;
218
219 while (low < high - 1) {
220 const int mid = (low + high) / 2;
221
222 if (valid_offset(fd, mid))
223 low = mid;
224 else
225 high = mid;
226 }
227 valid_offset(fd, 0);
228 return (low + 1);
229}
230
231static int get_size(const char *file)
232{
233 int fd;
234 long size;
235
236 fd = xopen(file, O_RDWR);
237 if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
238 close(fd);
239 return (size * 512);
240 }
241
242 size = count_blocks(fd);
243 close(fd);
244 return size;
245}
246
247static void write_tables(void) 199static void write_tables(void)
248{ 200{
249 /* Mark the superblock valid. */ 201 /* Mark the superblock valid. */
@@ -636,7 +588,6 @@ int mkfs_minix_main(int argc UNUSED_PARAM, char **argv)
636{ 588{
637 unsigned opt; 589 unsigned opt;
638 char *tmp; 590 char *tmp;
639 struct stat statbuf;
640 char *str_i; 591 char *str_i;
641 char *listfile = NULL; 592 char *listfile = NULL;
642 593
@@ -673,13 +624,17 @@ int mkfs_minix_main(int argc UNUSED_PARAM, char **argv)
673#endif 624#endif
674 } 625 }
675 626
676 G.device_name = *argv++; 627 G.device_name = argv[0];
677 if (!G.device_name) 628 if (!G.device_name)
678 bb_show_usage(); 629 bb_show_usage();
679 if (*argv) 630
680 G.total_blocks = xatou32(*argv); 631 /* Check if it is mounted */
681 else 632 if (find_mount_point(G.device_name, 0))
682 G.total_blocks = get_size(G.device_name) / 1024; 633 bb_error_msg_and_die("can't format mounted filesystem");
634
635 xmove_fd(xopen(G.device_name, O_RDWR), dev_fd);
636
637 G.total_blocks = get_volume_size_in_bytes(dev_fd, argv[1], 1024, /*extend:*/ 1) / 1024;
683 638
684 if (G.total_blocks < 10) 639 if (G.total_blocks < 10)
685 bb_error_msg_and_die("must have at least 10 blocks"); 640 bb_error_msg_and_die("must have at least 10 blocks");
@@ -690,25 +645,21 @@ int mkfs_minix_main(int argc UNUSED_PARAM, char **argv)
690 G.magic = MINIX2_SUPER_MAGIC; 645 G.magic = MINIX2_SUPER_MAGIC;
691 } else if (G.total_blocks > 65535) 646 } else if (G.total_blocks > 65535)
692 G.total_blocks = 65535; 647 G.total_blocks = 65535;
693 648#if 0
694 /* Check if it is mounted */ 649 struct stat statbuf;
695 if (find_mount_point(G.device_name, 0))
696 bb_error_msg_and_die("can't format mounted filesystem");
697
698 xmove_fd(xopen(G.device_name, O_RDWR), dev_fd);
699 xfstat(dev_fd, &statbuf, G.device_name); 650 xfstat(dev_fd, &statbuf, G.device_name);
651/* why? */
700 if (!S_ISBLK(statbuf.st_mode)) 652 if (!S_ISBLK(statbuf.st_mode))
701 opt &= ~1; // clear -c (check) 653 opt &= ~1; // clear -c (check)
702 654#if 0
703/* I don't know why someone has special code to prevent mkfs.minix 655/* I don't know why someone has special code to prevent mkfs.minix
704 * on IDE devices. Why IDE but not SCSI, etc?... */ 656 * on IDE devices. Why IDE but not SCSI, etc?... */
705#if 0
706 else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340) 657 else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
707 /* what is this? */ 658 /* what is this? */
708 bb_error_msg_and_die("will not try " 659 bb_error_msg_and_die("will not try "
709 "to make filesystem on '%s'", G.device_name); 660 "to make filesystem on '%s'", G.device_name);
710#endif 661#endif
711 662#endif
712 tmp = G.root_block; 663 tmp = G.root_block;
713 *(short *) tmp = 1; 664 *(short *) tmp = 1;
714 strcpy(tmp + 2, "."); 665 strcpy(tmp + 2, ".");