aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--archival/bbunzip.c10
-rw-r--r--archival/bzip2.c4
-rw-r--r--archival/gzip.c4
-rw-r--r--archival/libarchive/decompress_unlzma.c5
-rw-r--r--archival/libarchive/get_header_cpio.c3
-rw-r--r--archival/lzop.c1
-rw-r--r--archival/tar.c2
-rw-r--r--archival/unzip.c4
-rw-r--r--console-tools/loadfont.c2
-rw-r--r--coreutils/chgrp.c8
-rw-r--r--coreutils/chmod.c7
-rw-r--r--coreutils/chown.c10
-rw-r--r--coreutils/cksum.c54
-rw-r--r--coreutils/cp.c199
-rw-r--r--coreutils/cut.c1
-rw-r--r--coreutils/df.c45
-rw-r--r--coreutils/du.c33
-rw-r--r--coreutils/echo.c6
-rw-r--r--coreutils/env.c19
-rw-r--r--coreutils/expr.c4
-rw-r--r--coreutils/id.c2
-rw-r--r--coreutils/mv.c76
-rw-r--r--coreutils/nproc.c4
-rw-r--r--coreutils/shred.c33
-rw-r--r--coreutils/sort.c2
-rw-r--r--coreutils/touch.c2
-rw-r--r--coreutils/tty.c2
-rw-r--r--coreutils/uname.c4
-rw-r--r--coreutils/uniq.c24
-rw-r--r--coreutils/uudecode.c10
-rw-r--r--coreutils/who.c2
-rw-r--r--coreutils/yes.c2
-rw-r--r--docs/busybox_footer.pod1
-rw-r--r--e2fsprogs/chattr.c146
-rw-r--r--e2fsprogs/e2fs_lib.c209
-rw-r--r--e2fsprogs/e2fs_lib.h21
-rw-r--r--e2fsprogs/lsattr.c80
-rw-r--r--editors/awk.c272
-rw-r--r--editors/cmp.c2
-rw-r--r--editors/sed.c2
-rw-r--r--editors/vi.c144
-rw-r--r--findutils/grep.c6
-rw-r--r--include/bb_e2fs_defs.h23
-rw-r--r--include/dump.h4
-rw-r--r--include/libbb.h41
-rw-r--r--libbb/copy_file.c2
-rw-r--r--libbb/dump.c41
-rw-r--r--libbb/iterate_on_dir.c28
-rw-r--r--libbb/remove_file.c6
-rw-r--r--loginutils/su.c2
-rw-r--r--miscutils/ascii.c51
-rw-r--r--miscutils/bc.c75
-rw-r--r--miscutils/chat.c2
-rw-r--r--miscutils/flashcp.c4
-rw-r--r--miscutils/i2c_tools.c2
-rw-r--r--miscutils/man.c2
-rw-r--r--miscutils/microcom.c14
-rw-r--r--miscutils/mt.c2
-rw-r--r--miscutils/strings.c4
-rw-r--r--miscutils/ts.c5
-rw-r--r--miscutils/ubi_tools.c2
-rw-r--r--modutils/modinfo.c2
-rw-r--r--networking/ftpd.c4
-rw-r--r--networking/nameif.c4
-rw-r--r--networking/telnetd.c1
-rw-r--r--networking/tftp.c5
-rw-r--r--networking/udhcp/d6_dhcpc.c74
-rw-r--r--networking/udhcp/dhcpc.c131
-rw-r--r--networking/udhcp/dhcpc.h1
-rw-r--r--networking/vconfig.c12
-rw-r--r--procps/free.c86
-rw-r--r--procps/kill.c2
-rw-r--r--procps/lsof.c2
-rw-r--r--procps/pgrep.c12
-rw-r--r--procps/sysctl.c4
-rw-r--r--procps/top.c4
-rw-r--r--procps/watch.c2
-rw-r--r--runit/svlogd.c12
-rw-r--r--shell/ash.c11
-rw-r--r--shell/ash_test/ash-misc/control_char3.right1
-rwxr-xr-xshell/ash_test/ash-misc/control_char3.tests2
-rw-r--r--shell/ash_test/ash-misc/control_char4.right1
-rwxr-xr-xshell/ash_test/ash-misc/control_char4.tests2
-rw-r--r--shell/ash_test/ash-parsing/bkslash_newline4.right4
-rwxr-xr-xshell/ash_test/ash-parsing/bkslash_newline4.tests14
-rw-r--r--shell/hush.c47
-rw-r--r--shell/hush_test/hush-misc/control_char3.right1
-rwxr-xr-xshell/hush_test/hush-misc/control_char3.tests2
-rw-r--r--shell/hush_test/hush-misc/control_char4.right1
-rwxr-xr-xshell/hush_test/hush-misc/control_char4.tests2
-rw-r--r--shell/hush_test/hush-parsing/bkslash_newline4.right4
-rwxr-xr-xshell/hush_test/hush-parsing/bkslash_newline4.tests14
-rw-r--r--shell/hush_test/hush-vars/var6.right2
-rw-r--r--sysklogd/logger.c2
-rw-r--r--testsuite/mv/mv-files-to-dir-216
-rwxr-xr-xtestsuite/unlzma.tests17
-rw-r--r--testsuite/unlzma_issue_3.lzmabin0 -> 27 bytes
-rw-r--r--util-linux/blockdev.c28
-rw-r--r--util-linux/dmesg.c2
-rw-r--r--util-linux/fdisk.c4
-rw-r--r--util-linux/flock.c2
-rw-r--r--util-linux/hexdump_xxd.c63
-rw-r--r--util-linux/ionice.c18
-rw-r--r--util-linux/mountpoint.c9
-rw-r--r--util-linux/readprofile.c4
-rw-r--r--util-linux/renice.c2
-rw-r--r--util-linux/switch_root.c35
-rw-r--r--util-linux/taskset.c108
108 files changed, 1571 insertions, 982 deletions
diff --git a/archival/bbunzip.c b/archival/bbunzip.c
index 2beb92763..467996071 100644
--- a/archival/bbunzip.c
+++ b/archival/bbunzip.c
@@ -286,7 +286,7 @@ int uncompress_main(int argc UNUSED_PARAM, char **argv)
286//usage: "\n -c Write to stdout" 286//usage: "\n -c Write to stdout"
287//usage: "\n -f Force" 287//usage: "\n -f Force"
288//usage: "\n -k Keep input files" 288//usage: "\n -k Keep input files"
289//usage: "\n -t Test file integrity" 289//usage: "\n -t Test integrity"
290//usage: 290//usage:
291//usage:#define gunzip_example_usage 291//usage:#define gunzip_example_usage
292//usage: "$ ls -la /tmp/BusyBox*\n" 292//usage: "$ ls -la /tmp/BusyBox*\n"
@@ -409,6 +409,8 @@ int gunzip_main(int argc UNUSED_PARAM, char **argv)
409//usage: "\n -c Write to stdout" 409//usage: "\n -c Write to stdout"
410//usage: "\n -f Force" 410//usage: "\n -f Force"
411//usage: "\n -k Keep input files" 411//usage: "\n -k Keep input files"
412//usage: "\n -t Test integrity"
413//usage:
412//usage:#define bzcat_trivial_usage 414//usage:#define bzcat_trivial_usage
413//usage: "[FILE]..." 415//usage: "[FILE]..."
414//usage:#define bzcat_full_usage "\n\n" 416//usage:#define bzcat_full_usage "\n\n"
@@ -467,6 +469,7 @@ int bunzip2_main(int argc UNUSED_PARAM, char **argv)
467//usage: "\n -c Write to stdout" 469//usage: "\n -c Write to stdout"
468//usage: "\n -f Force" 470//usage: "\n -f Force"
469//usage: "\n -k Keep input files" 471//usage: "\n -k Keep input files"
472//usage: "\n -t Test integrity"
470//usage: 473//usage:
471//usage:#define lzma_trivial_usage 474//usage:#define lzma_trivial_usage
472//usage: "-d [-cfk] [FILE]..." 475//usage: "-d [-cfk] [FILE]..."
@@ -476,6 +479,7 @@ int bunzip2_main(int argc UNUSED_PARAM, char **argv)
476//usage: "\n -c Write to stdout" 479//usage: "\n -c Write to stdout"
477//usage: "\n -f Force" 480//usage: "\n -f Force"
478//usage: "\n -k Keep input files" 481//usage: "\n -k Keep input files"
482//usage: "\n -t Test integrity"
479//usage: 483//usage:
480//usage:#define lzcat_trivial_usage 484//usage:#define lzcat_trivial_usage
481//usage: "[FILE]..." 485//usage: "[FILE]..."
@@ -538,7 +542,7 @@ int unlzma_main(int argc UNUSED_PARAM, char **argv)
538//usage: "\n -c Write to stdout" 542//usage: "\n -c Write to stdout"
539//usage: "\n -f Force" 543//usage: "\n -f Force"
540//usage: "\n -k Keep input files" 544//usage: "\n -k Keep input files"
541//usage: "\n -t Test file integrity" 545//usage: "\n -t Test integrity"
542//usage: 546//usage:
543//usage:#define xz_trivial_usage 547//usage:#define xz_trivial_usage
544//usage: "-d [-cfk] [FILE]..." 548//usage: "-d [-cfk] [FILE]..."
@@ -548,7 +552,7 @@ int unlzma_main(int argc UNUSED_PARAM, char **argv)
548//usage: "\n -c Write to stdout" 552//usage: "\n -c Write to stdout"
549//usage: "\n -f Force" 553//usage: "\n -f Force"
550//usage: "\n -k Keep input files" 554//usage: "\n -k Keep input files"
551//usage: "\n -t Test file integrity" 555//usage: "\n -t Test integrity"
552//usage: 556//usage:
553//usage:#define xzcat_trivial_usage 557//usage:#define xzcat_trivial_usage
554//usage: "[FILE]..." 558//usage: "[FILE]..."
diff --git a/archival/bzip2.c b/archival/bzip2.c
index ac5db0880..bce13cf93 100644
--- a/archival/bzip2.c
+++ b/archival/bzip2.c
@@ -56,11 +56,13 @@
56//usage: "\n -1..9 Compression level" 56//usage: "\n -1..9 Compression level"
57//usage: IF_FEATURE_BZIP2_DECOMPRESS( 57//usage: IF_FEATURE_BZIP2_DECOMPRESS(
58//usage: "\n -d Decompress" 58//usage: "\n -d Decompress"
59//usage: "\n -t Test file integrity"
60//usage: ) 59//usage: )
61//usage: "\n -c Write to stdout" 60//usage: "\n -c Write to stdout"
62//usage: "\n -f Force" 61//usage: "\n -f Force"
63//usage: "\n -k Keep input files" 62//usage: "\n -k Keep input files"
63//usage: IF_FEATURE_BZIP2_DECOMPRESS(
64//usage: "\n -t Test integrity"
65//usage: )
64 66
65#include "libbb.h" 67#include "libbb.h"
66#include "bb_archive.h" 68#include "bb_archive.h"
diff --git a/archival/gzip.c b/archival/gzip.c
index d9c730f13..91bd4d09d 100644
--- a/archival/gzip.c
+++ b/archival/gzip.c
@@ -77,11 +77,13 @@ aa: 85.1% -- replaced with aa.gz
77//usage: ) 77//usage: )
78//usage: IF_FEATURE_GZIP_DECOMPRESS( 78//usage: IF_FEATURE_GZIP_DECOMPRESS(
79//usage: "\n -d Decompress" 79//usage: "\n -d Decompress"
80//usage: "\n -t Test file integrity"
81//usage: ) 80//usage: )
82//usage: "\n -c Write to stdout" 81//usage: "\n -c Write to stdout"
83//usage: "\n -f Force" 82//usage: "\n -f Force"
84//usage: "\n -k Keep input files" 83//usage: "\n -k Keep input files"
84//usage: IF_FEATURE_GZIP_DECOMPRESS(
85//usage: "\n -t Test integrity"
86//usage: )
85//usage: 87//usage:
86//usage:#define gzip_example_usage 88//usage:#define gzip_example_usage
87//usage: "$ ls -la /tmp/busybox*\n" 89//usage: "$ ls -la /tmp/busybox*\n"
diff --git a/archival/libarchive/decompress_unlzma.c b/archival/libarchive/decompress_unlzma.c
index 0744f231a..fb5aac8fe 100644
--- a/archival/libarchive/decompress_unlzma.c
+++ b/archival/libarchive/decompress_unlzma.c
@@ -290,8 +290,11 @@ unpack_lzma_stream(transformer_state_t *xstate)
290 uint32_t pos; 290 uint32_t pos;
291 291
292 pos = buffer_pos - rep0; 292 pos = buffer_pos - rep0;
293 if ((int32_t)pos < 0) 293 if ((int32_t)pos < 0) {
294 pos += header.dict_size; 294 pos += header.dict_size;
295 if ((int32_t)pos < 0)
296 goto bad;
297 }
295 match_byte = buffer[pos]; 298 match_byte = buffer[pos];
296 do { 299 do {
297 int bit; 300 int bit;
diff --git a/archival/libarchive/get_header_cpio.c b/archival/libarchive/get_header_cpio.c
index 4ad174732..9ad0557c2 100644
--- a/archival/libarchive/get_header_cpio.c
+++ b/archival/libarchive/get_header_cpio.c
@@ -20,7 +20,7 @@ typedef struct hardlinks_t {
20char FAST_FUNC get_header_cpio(archive_handle_t *archive_handle) 20char FAST_FUNC get_header_cpio(archive_handle_t *archive_handle)
21{ 21{
22 file_header_t *file_header = archive_handle->file_header; 22 file_header_t *file_header = archive_handle->file_header;
23 char cpio_header[110]; 23 char cpio_header[111];
24 int namesize; 24 int namesize;
25 int major, minor, nlink, mode, inode; 25 int major, minor, nlink, mode, inode;
26 unsigned size, uid, gid, mtime; 26 unsigned size, uid, gid, mtime;
@@ -43,6 +43,7 @@ char FAST_FUNC get_header_cpio(archive_handle_t *archive_handle)
43 bb_simple_error_msg_and_die("unsupported cpio format, use newc or crc"); 43 bb_simple_error_msg_and_die("unsupported cpio format, use newc or crc");
44 } 44 }
45 45
46 cpio_header[110] = '\0'; /* sscanf may call strlen which may break without this */
46 if (sscanf(cpio_header + 6, 47 if (sscanf(cpio_header + 6,
47 "%8x" "%8x" "%8x" "%8x" 48 "%8x" "%8x" "%8x" "%8x"
48 "%8x" "%8x" "%8x" /*maj,min:*/ "%*16c" 49 "%8x" "%8x" "%8x" /*maj,min:*/ "%*16c"
diff --git a/archival/lzop.c b/archival/lzop.c
index bdd21598c..74df8ff03 100644
--- a/archival/lzop.c
+++ b/archival/lzop.c
@@ -86,6 +86,7 @@
86//usage: "\n -f Force" 86//usage: "\n -f Force"
87//usage: "\n -U Delete input files" 87//usage: "\n -U Delete input files"
88///////: "\n -k Keep input files" (default, so why bother documenting?) 88///////: "\n -k Keep input files" (default, so why bother documenting?)
89//usage: "\n -t Test integrity"
89//usage: "\n -v Verbose" 90//usage: "\n -v Verbose"
90//usage: "\n -F Don't verify checksum" 91//usage: "\n -F Don't verify checksum"
91 92
diff --git a/archival/tar.c b/archival/tar.c
index 3b8777414..8cd371173 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -796,7 +796,7 @@ static llist_t *append_file_list_to_list(llist_t *list)
796//usage: IF_FEATURE_TAR_NOPRESERVE_TIME("m") 796//usage: IF_FEATURE_TAR_NOPRESERVE_TIME("m")
797//usage: "vokO] " 797//usage: "vokO] "
798//usage: "[-f TARFILE] [-C DIR] " 798//usage: "[-f TARFILE] [-C DIR] "
799//usage: IF_FEATURE_TAR_FROM("[-T FILE] [-X FILE] "IF_FEATURE_TAR_LONG_OPTIONS("[OPTION]... ")) 799//usage: IF_FEATURE_TAR_FROM("[-T FILE] [-X FILE] "IF_FEATURE_TAR_LONG_OPTIONS("[LONGOPT]... "))
800//usage: "[FILE]..." 800//usage: "[FILE]..."
801//usage:#define tar_full_usage "\n\n" 801//usage:#define tar_full_usage "\n\n"
802//usage: IF_FEATURE_TAR_CREATE("Create, extract, ") 802//usage: IF_FEATURE_TAR_CREATE("Create, extract, ")
diff --git a/archival/unzip.c b/archival/unzip.c
index e02cf33e3..edfd73652 100644
--- a/archival/unzip.c
+++ b/archival/unzip.c
@@ -56,14 +56,14 @@
56//kbuild:lib-$(CONFIG_UNZIP) += unzip.o 56//kbuild:lib-$(CONFIG_UNZIP) += unzip.o
57 57
58//usage:#define unzip_trivial_usage 58//usage:#define unzip_trivial_usage
59//usage: "[-lnojpq] FILE[.zip] [FILE]... [-x FILE...] [-d DIR]" 59//usage: "[-lnojpq] FILE[.zip] [FILE]... [-x FILE]... [-d DIR]"
60//usage:#define unzip_full_usage "\n\n" 60//usage:#define unzip_full_usage "\n\n"
61//usage: "Extract FILEs from ZIP archive\n" 61//usage: "Extract FILEs from ZIP archive\n"
62//usage: "\n -l List contents (with -q for short form)" 62//usage: "\n -l List contents (with -q for short form)"
63//usage: "\n -n Never overwrite files (default: ask)" 63//usage: "\n -n Never overwrite files (default: ask)"
64//usage: "\n -o Overwrite" 64//usage: "\n -o Overwrite"
65//usage: "\n -j Do not restore paths" 65//usage: "\n -j Do not restore paths"
66//usage: "\n -p Print to stdout" 66//usage: "\n -p Write to stdout"
67//usage: "\n -t Test" 67//usage: "\n -t Test"
68//usage: "\n -q Quiet" 68//usage: "\n -q Quiet"
69//usage: "\n -x FILE Exclude FILEs" 69//usage: "\n -x FILE Exclude FILEs"
diff --git a/console-tools/loadfont.c b/console-tools/loadfont.c
index 7533b0aad..81a0e6aa8 100644
--- a/console-tools/loadfont.c
+++ b/console-tools/loadfont.c
@@ -399,7 +399,7 @@ setfont [-O font+umap.orig] [-o font.orig] [-om cmap.orig]
399-V Version 399-V Version
400*/ 400*/
401//usage:#define setfont_trivial_usage 401//usage:#define setfont_trivial_usage
402//usage: "FONT [-m MAPFILE] [-C TTY]" 402//usage: "[-m MAPFILE] [-C TTY] FILE"
403//usage:#define setfont_full_usage "\n\n" 403//usage:#define setfont_full_usage "\n\n"
404//usage: "Load a console font\n" 404//usage: "Load a console font\n"
405//usage: "\n -m MAPFILE Load console screen map" 405//usage: "\n -m MAPFILE Load console screen map"
diff --git a/coreutils/chgrp.c b/coreutils/chgrp.c
index 0c2060981..e6ac316e5 100644
--- a/coreutils/chgrp.c
+++ b/coreutils/chgrp.c
@@ -23,13 +23,17 @@
23//usage:#define chgrp_trivial_usage 23//usage:#define chgrp_trivial_usage
24//usage: "[-Rh"IF_DESKTOP("LHPcvf")"]... GROUP FILE..." 24//usage: "[-Rh"IF_DESKTOP("LHPcvf")"]... GROUP FILE..."
25//usage:#define chgrp_full_usage "\n\n" 25//usage:#define chgrp_full_usage "\n\n"
26//usage: "Change the group membership of FILEs to GROUP\n" 26//usage: "Change the group membership of FILEs to GROUP"
27//usage: "\n -R Recurse" 27//usage: "\n"
28//usage: "\n -h Affect symlinks instead of symlink targets" 28//usage: "\n -h Affect symlinks instead of symlink targets"
29//usage: IF_DESKTOP( 29//usage: IF_DESKTOP(
30//usage: "\n -L Traverse all symlinks to directories" 30//usage: "\n -L Traverse all symlinks to directories"
31//usage: "\n -H Traverse symlinks on command line only" 31//usage: "\n -H Traverse symlinks on command line only"
32//usage: "\n -P Don't traverse symlinks (default)" 32//usage: "\n -P Don't traverse symlinks (default)"
33//usage: )
34//next 4 options are the same for chmod/chown/chgrp:
35//usage: "\n -R Recurse"
36//usage: IF_DESKTOP(
33//usage: "\n -c List changed files" 37//usage: "\n -c List changed files"
34//usage: "\n -v Verbose" 38//usage: "\n -v Verbose"
35//usage: "\n -f Hide errors" 39//usage: "\n -f Hide errors"
diff --git a/coreutils/chmod.c b/coreutils/chmod.c
index d2988c490..e260adab2 100644
--- a/coreutils/chmod.c
+++ b/coreutils/chmod.c
@@ -26,12 +26,13 @@
26//usage:#define chmod_trivial_usage 26//usage:#define chmod_trivial_usage
27//usage: "[-R"IF_DESKTOP("cvf")"] MODE[,MODE]... FILE..." 27//usage: "[-R"IF_DESKTOP("cvf")"] MODE[,MODE]... FILE..."
28//usage:#define chmod_full_usage "\n\n" 28//usage:#define chmod_full_usage "\n\n"
29//usage: "Each MODE is one or more of the letters ugoa, one of the\n" 29//usage: "MODE is octal number (bit pattern sstrwxrwxrwx) or [ugoa]{+|-|=}[rwxXst]"
30//usage: "symbols +-= and one or more of the letters rwxst\n" 30//usage: "\n"
31//next 4 options are the same for chmod/chown/chgrp:
31//usage: "\n -R Recurse" 32//usage: "\n -R Recurse"
32//usage: IF_DESKTOP( 33//usage: IF_DESKTOP(
33//usage: "\n -c List changed files" 34//usage: "\n -c List changed files"
34//usage: "\n -v List all files" 35//usage: "\n -v Verbose"
35//usage: "\n -f Hide errors" 36//usage: "\n -f Hide errors"
36//usage: ) 37//usage: )
37//usage: 38//usage:
diff --git a/coreutils/chown.c b/coreutils/chown.c
index 170507147..528a2a05a 100644
--- a/coreutils/chown.c
+++ b/coreutils/chown.c
@@ -28,15 +28,19 @@
28//usage:#define chown_trivial_usage 28//usage:#define chown_trivial_usage
29//usage: "[-Rh"IF_DESKTOP("LHPcvf")"]... USER[:[GRP]] FILE..." 29//usage: "[-Rh"IF_DESKTOP("LHPcvf")"]... USER[:[GRP]] FILE..."
30//usage:#define chown_full_usage "\n\n" 30//usage:#define chown_full_usage "\n\n"
31//usage: "Change the owner and/or group of FILEs to USER and/or GRP\n" 31//usage: "Change the owner and/or group of FILEs to USER and/or GRP"
32//usage: "\n -R Recurse" 32//usage: "\n"
33//usage: "\n -h Affect symlinks instead of symlink targets" 33//usage: "\n -h Affect symlinks instead of symlink targets"
34//usage: IF_DESKTOP( 34//usage: IF_DESKTOP(
35//usage: "\n -L Traverse all symlinks to directories" 35//usage: "\n -L Traverse all symlinks to directories"
36//usage: "\n -H Traverse symlinks on command line only" 36//usage: "\n -H Traverse symlinks on command line only"
37//usage: "\n -P Don't traverse symlinks (default)" 37//usage: "\n -P Don't traverse symlinks (default)"
38//usage: )
39//next 4 options are the same for chmod/chown/chgrp:
40//usage: "\n -R Recurse"
41//usage: IF_DESKTOP(
38//usage: "\n -c List changed files" 42//usage: "\n -c List changed files"
39//usage: "\n -v List all files" 43//usage: "\n -v Verbose"
40//usage: "\n -f Hide errors" 44//usage: "\n -f Hide errors"
41//usage: ) 45//usage: )
42//usage: 46//usage:
diff --git a/coreutils/cksum.c b/coreutils/cksum.c
index 633322bc7..83b7e3238 100644
--- a/coreutils/cksum.c
+++ b/coreutils/cksum.c
@@ -9,32 +9,40 @@
9//config:config CKSUM 9//config:config CKSUM
10//config: bool "cksum (4.1 kb)" 10//config: bool "cksum (4.1 kb)"
11//config: default y 11//config: default y
12//config: help 12//config:
13//config: cksum is used to calculate the CRC32 checksum of a file. 13//config:config CRC32
14//config: bool "crc32 (4.1 kb)"
15//config: default y
14 16
17// APPLET_NOEXEC:name main location suid_type help
15//applet:IF_CKSUM(APPLET_NOEXEC(cksum, cksum, BB_DIR_USR_BIN, BB_SUID_DROP, cksum)) 18//applet:IF_CKSUM(APPLET_NOEXEC(cksum, cksum, BB_DIR_USR_BIN, BB_SUID_DROP, cksum))
19//applet:IF_CRC32(APPLET_NOEXEC(crc32, cksum, BB_DIR_USR_BIN, BB_SUID_DROP, cksum))
16/* bb_common_bufsiz1 usage here is safe wrt NOEXEC: not expecting it to be zeroed. */ 20/* bb_common_bufsiz1 usage here is safe wrt NOEXEC: not expecting it to be zeroed. */
17 21
18//kbuild:lib-$(CONFIG_CKSUM) += cksum.o 22//kbuild:lib-$(CONFIG_CKSUM) += cksum.o
23//kbuild:lib-$(CONFIG_CRC32) += cksum.o
19 24
20//usage:#define cksum_trivial_usage 25//usage:#define cksum_trivial_usage
21//usage: "FILE..." 26//usage: "FILE..."
22//usage:#define cksum_full_usage "\n\n" 27//usage:#define cksum_full_usage "\n\n"
23//usage: "Calculate the CRC32 checksums of FILEs" 28//usage: "Calculate CRC32 checksum of FILEs"
24 29
25#include "libbb.h" 30#include "libbb.h"
26#include "common_bufsiz.h" 31#include "common_bufsiz.h"
27 32
28/* This is a NOEXEC applet. Be very careful! */ 33/* This is a NOEXEC applet. Be very careful! */
29 34
35#define IS_CKSUM (ENABLE_CKSUM && (!ENABLE_CRC32 || applet_name[1] == 'k'))
36#define IS_CRC32 (ENABLE_CRC32 && (!ENABLE_CKSUM || applet_name[1] == 'r'))
37
30int cksum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 38int cksum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
31int cksum_main(int argc UNUSED_PARAM, char **argv) 39int cksum_main(int argc UNUSED_PARAM, char **argv)
32{ 40{
33 uint32_t *crc32_table = crc32_filltable(NULL, 1); 41 uint32_t *crc32_table = crc32_filltable(NULL, IS_CKSUM);
34 int exit_code = EXIT_SUCCESS; 42 int exit_code = EXIT_SUCCESS;
35 43
36#if ENABLE_DESKTOP 44#if ENABLE_DESKTOP
37 getopt32(argv, ""); /* coreutils 6.9 compat */ 45 getopt32(argv, ""); /* cksum coreutils 6.9 compat */
38 argv += optind; 46 argv += optind;
39#else 47#else
40 argv++; 48 argv++;
@@ -43,41 +51,55 @@ int cksum_main(int argc UNUSED_PARAM, char **argv)
43 setup_common_bufsiz(); 51 setup_common_bufsiz();
44 do { 52 do {
45 uint32_t crc; 53 uint32_t crc;
46 off_t filesize; 54 IF_CKSUM(off_t filesize;)
47 int fd = open_or_warn_stdin(*argv ? *argv : bb_msg_standard_input); 55 const char *fname = *argv ? *argv : bb_msg_standard_input;
56 int fd = open_or_warn_stdin(fname);
48 57
49 if (fd < 0) { 58 if (fd < 0) {
50 exit_code = EXIT_FAILURE; 59 exit_code = EXIT_FAILURE;
51 continue; 60 continue;
52 } 61 }
53 62
54 crc = 0; 63 crc = IS_CKSUM ? 0 : 0xffffffff;
55 filesize = 0; 64 IF_CKSUM(filesize = 0;)
56#define read_buf bb_common_bufsiz1 65#define read_buf bb_common_bufsiz1
57 for (;;) { 66 for (;;) {
58 uoff_t t;
59 int bytes_read = safe_read(fd, read_buf, COMMON_BUFSIZE); 67 int bytes_read = safe_read(fd, read_buf, COMMON_BUFSIZE);
68 if (bytes_read < 0)
69 bb_simple_perror_msg_and_die(fname);
60 if (bytes_read > 0) { 70 if (bytes_read > 0) {
61 filesize += bytes_read; 71 IF_CKSUM(filesize += bytes_read;)
62 } else { 72 } else {
63 /* Checksum filesize bytes, LSB first, and exit */ 73 IF_CKSUM(uoff_t t;)
74
64 close(fd); 75 close(fd);
76 if (IS_CRC32)
77 break;
78#if ENABLE_CKSUM
65 fd = -1; /* break flag */ 79 fd = -1; /* break flag */
80 /* Checksum filesize bytes, LSB first */
66 t = filesize; 81 t = filesize;
67 bytes_read = 0; 82 /*bytes_read = 0; - already is */
68 while (t != 0) { 83 while (t != 0) {
69 read_buf[bytes_read++] = (uint8_t)t; 84 read_buf[bytes_read++] = (uint8_t)t;
70 t >>= 8; 85 t >>= 8;
71 } 86 }
87#endif
72 } 88 }
73 crc = crc32_block_endian1(crc, read_buf, bytes_read, crc32_table); 89 crc = (IS_CKSUM ? crc32_block_endian1 : crc32_block_endian0)(crc, read_buf, bytes_read, crc32_table);
74 if (fd < 0) 90 if (ENABLE_CKSUM && fd < 0)
75 break; 91 break;
76 } 92 }
77 93
78 crc = ~crc; 94 crc = ~crc;
79 printf((*argv ? "%u %"OFF_FMT"u %s\n" : "%u %"OFF_FMT"u\n"), 95#if ENABLE_CKSUM
96 if (IS_CKSUM)
97 printf((*argv ? "%u %"OFF_FMT"u %s\n" : "%u %"OFF_FMT"u\n"),
80 (unsigned)crc, filesize, *argv); 98 (unsigned)crc, filesize, *argv);
99 else
100#endif
101 printf((*argv ? "%08x %s\n" : "%08x\n"),
102 (unsigned)crc, *argv);
81 } while (*argv && *++argv); 103 } while (*argv && *++argv);
82 104
83 fflush_stdout_and_exit(exit_code); 105 fflush_stdout_and_exit(exit_code);
diff --git a/coreutils/cp.c b/coreutils/cp.c
index f92ba6886..50ca1ccea 100644
--- a/coreutils/cp.c
+++ b/coreutils/cp.c
@@ -37,8 +37,55 @@
37 37
38/* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */ 38/* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */
39 39
40// Options of cp from GNU coreutils 6.10:
41// -a, --archive
42// -f, --force
43// -i, --interactive
44// -l, --link
45// -L, --dereference
46// -P, --no-dereference
47// -R, -r, --recursive
48// -s, --symbolic-link
49// -v, --verbose
50// -H follow command-line symbolic links in SOURCE
51// -d same as --no-dereference --preserve=links
52// -p same as --preserve=mode,ownership,timestamps
53// -c same as --preserve=context
54// -u, --update
55// copy only when the SOURCE file is newer than the destination
56// file or when the destination file is missing
57// --remove-destination
58// remove each existing destination file before attempting to open
59// --parents
60// use full source file name under DIRECTORY
61// -T, --no-target-directory
62// treat DEST as a normal file
63// NOT SUPPORTED IN BBOX:
64// --backup[=CONTROL]
65// make a backup of each existing destination file
66// -b like --backup but does not accept an argument
67// --copy-contents
68// copy contents of special files when recursive
69// --preserve[=ATTR_LIST]
70// preserve attributes (default: mode,ownership,timestamps),
71// if possible additional attributes: security context,links,all
72// --no-preserve=ATTR_LIST
73// --sparse=WHEN
74// control creation of sparse files
75// --strip-trailing-slashes
76// remove any trailing slashes from each SOURCE argument
77// -S, --suffix=SUFFIX
78// override the usual backup suffix
79// -t, --target-directory=DIRECTORY
80// copy all SOURCE arguments into DIRECTORY
81// -x, --one-file-system
82// stay on this file system
83// -Z, --context=CONTEXT
84// (SELinux) set SELinux security context of copy to CONTEXT
85
40//usage:#define cp_trivial_usage 86//usage:#define cp_trivial_usage
41//usage: "[-arPLHpfilsTu] SOURCE... DEST" 87//usage: "[-arPLHpfinlsTu] SOURCE DEST\n"
88//usage: "or: cp [-arPLHpfinlsu] SOURCE... { -t DIRECTORY | DIRECTORY }"
42//usage:#define cp_full_usage "\n\n" 89//usage:#define cp_full_usage "\n\n"
43//usage: "Copy SOURCEs to DEST\n" 90//usage: "Copy SOURCEs to DEST\n"
44//usage: "\n -a Same as -dpR" 91//usage: "\n -a Same as -dpR"
@@ -52,8 +99,10 @@
52//usage: "\n -p Preserve file attributes if possible" 99//usage: "\n -p Preserve file attributes if possible"
53//usage: "\n -f Overwrite" 100//usage: "\n -f Overwrite"
54//usage: "\n -i Prompt before overwrite" 101//usage: "\n -i Prompt before overwrite"
102//usage: "\n -n Don't overwrite"
55//usage: "\n -l,-s Create (sym)links" 103//usage: "\n -l,-s Create (sym)links"
56//usage: "\n -T Treat DEST as a normal file" 104//usage: "\n -T Refuse to copy if DEST is a directory"
105//usage: "\n -t DIR Copy all SOURCEs into DIR"
57//usage: "\n -u Copy only newer files" 106//usage: "\n -u Copy only newer files"
58 107
59#include "libbb.h" 108#include "libbb.h"
@@ -73,14 +122,12 @@ int cp_main(int argc, char **argv)
73 int flags; 122 int flags;
74 int status; 123 int status;
75 enum { 124 enum {
76 FILEUTILS_CP_OPTNUM = sizeof(FILEUTILS_CP_OPTSTR)-1,
77#if ENABLE_FEATURE_CP_LONG_OPTIONS 125#if ENABLE_FEATURE_CP_LONG_OPTIONS
78 /*OPT_rmdest = FILEUTILS_RMDEST = 1 << FILEUTILS_CP_OPTNUM */ 126 /*OPT_rmdest = FILEUTILS_RMDEST = 1 << FILEUTILS_CP_OPTBITS */
79 OPT_parents = 1 << (FILEUTILS_CP_OPTNUM+1), 127 OPT_parents = 1 << (FILEUTILS_CP_OPTBITS+1),
80 OPT_reflink = 1 << (FILEUTILS_CP_OPTNUM+2), 128 OPT_reflink = 1 << (FILEUTILS_CP_OPTBITS+2),
81#endif 129#endif
82 }; 130 };
83
84#if ENABLE_FEATURE_CP_LONG_OPTIONS 131#if ENABLE_FEATURE_CP_LONG_OPTIONS
85# if ENABLE_FEATURE_CP_REFLINK 132# if ENABLE_FEATURE_CP_REFLINK
86 char *reflink = NULL; 133 char *reflink = NULL;
@@ -88,28 +135,34 @@ int cp_main(int argc, char **argv)
88 flags = getopt32long(argv, "^" 135 flags = getopt32long(argv, "^"
89 FILEUTILS_CP_OPTSTR 136 FILEUTILS_CP_OPTSTR
90 "\0" 137 "\0"
91 // Need at least two arguments 138 // Need at least one argument. (Usually two+, but -t DIR can have only one)
92 // Soft- and hardlinking doesn't mix 139 // Soft- and hardlinking doesn't mix
93 // -P and -d are the same (-P is POSIX, -d is GNU) 140 // -P and -d are the same (-P is POSIX, -d is GNU)
94 // -r and -R are the same 141 // -r and -R are the same
95 // -R (and therefore -r) turns on -d (coreutils does this) 142 // -R (and therefore -r) turns on -d (coreutils does this)
96 // -a = -pdR 143 // -a = -pdR
97 "-2:l--s:s--l:Pd:rRd:Rd:apdR", 144 // -i overrides -n and vice versa (last wins)
145 "-1:l--s:s--l:Pd:rRd:Rd:apdR:i-n:n-i",
98 "archive\0" No_argument "a" 146 "archive\0" No_argument "a"
99 "force\0" No_argument "f" 147 "force\0" No_argument "f"
100 "interactive\0" No_argument "i" 148 "interactive\0" No_argument "i"
149 "no-clobber\0" No_argument "n"
101 "link\0" No_argument "l" 150 "link\0" No_argument "l"
102 "dereference\0" No_argument "L" 151 "dereference\0" No_argument "L"
103 "no-dereference\0" No_argument "P" 152 "no-dereference\0" No_argument "P"
104 "recursive\0" No_argument "R" 153 "recursive\0" No_argument "R"
105 "symbolic-link\0" No_argument "s" 154 "symbolic-link\0" No_argument "s"
106 "no-target-directory\0" No_argument "T" 155 "no-target-directory\0" No_argument "T"
156 "target-directory\0" Required_argument "t"
107 "verbose\0" No_argument "v" 157 "verbose\0" No_argument "v"
108 "update\0" No_argument "u" 158 "update\0" No_argument "u"
109 "remove-destination\0" No_argument "\xff" 159 "remove-destination\0" No_argument "\xff"
110 "parents\0" No_argument "\xfe" 160 "parents\0" No_argument "\xfe"
111# if ENABLE_FEATURE_CP_REFLINK 161# if ENABLE_FEATURE_CP_REFLINK
112 "reflink\0" Optional_argument "\xfd" 162 "reflink\0" Optional_argument "\xfd"
163# endif
164 , &last
165# if ENABLE_FEATURE_CP_REFLINK
113 , &reflink 166 , &reflink
114# endif 167# endif
115 ); 168 );
@@ -128,55 +181,10 @@ int cp_main(int argc, char **argv)
128 flags = getopt32(argv, "^" 181 flags = getopt32(argv, "^"
129 FILEUTILS_CP_OPTSTR 182 FILEUTILS_CP_OPTSTR
130 "\0" 183 "\0"
131 "-2:l--s:s--l:Pd:rRd:Rd:apdR" 184 "-1:l--s:s--l:Pd:rRd:Rd:apdR"
185 , &last
132 ); 186 );
133#endif 187#endif
134 /* Options of cp from GNU coreutils 6.10:
135 * -a, --archive
136 * -f, --force
137 * -i, --interactive
138 * -l, --link
139 * -L, --dereference
140 * -P, --no-dereference
141 * -R, -r, --recursive
142 * -s, --symbolic-link
143 * -v, --verbose
144 * -H follow command-line symbolic links in SOURCE
145 * -d same as --no-dereference --preserve=links
146 * -p same as --preserve=mode,ownership,timestamps
147 * -c same as --preserve=context
148 * -u, --update
149 * copy only when the SOURCE file is newer than the destination
150 * file or when the destination file is missing
151 * --remove-destination
152 * remove each existing destination file before attempting to open
153 * --parents
154 * use full source file name under DIRECTORY
155 * -T, --no-target-directory
156 * treat DEST as a normal file
157 * NOT SUPPORTED IN BBOX:
158 * --backup[=CONTROL]
159 * make a backup of each existing destination file
160 * -b like --backup but does not accept an argument
161 * --copy-contents
162 * copy contents of special files when recursive
163 * --preserve[=ATTR_LIST]
164 * preserve attributes (default: mode,ownership,timestamps),
165 * if possible additional attributes: security context,links,all
166 * --no-preserve=ATTR_LIST
167 * --sparse=WHEN
168 * control creation of sparse files
169 * --strip-trailing-slashes
170 * remove any trailing slashes from each SOURCE argument
171 * -S, --suffix=SUFFIX
172 * override the usual backup suffix
173 * -t, --target-directory=DIRECTORY
174 * copy all SOURCE arguments into DIRECTORY
175 * -x, --one-file-system
176 * stay on this file system
177 * -Z, --context=CONTEXT
178 * (SELinux) set SELinux security context of copy to CONTEXT
179 */
180 argc -= optind; 188 argc -= optind;
181 argv += optind; 189 argv += optind;
182 /* Reverse this bit. If there is -d, bit is not set: */ 190 /* Reverse this bit. If there is -d, bit is not set: */
@@ -195,49 +203,56 @@ int cp_main(int argc, char **argv)
195#endif 203#endif
196 204
197 status = EXIT_SUCCESS; 205 status = EXIT_SUCCESS;
198 last = argv[argc - 1]; 206 if (!(flags & FILEUTILS_TARGET_DIR)) {
199 /* If there are only two arguments and... */ 207 last = argv[argc - 1];
200 if (argc == 2) { 208 if (argc < 2)
201 s_flags = cp_mv_stat2(*argv, &source_stat, 209 bb_show_usage();
202 (flags & FILEUTILS_DEREFERENCE) ? stat : lstat); 210 if (argc != 2) {
203 if (s_flags < 0) 211 if (flags & FILEUTILS_NO_TARGET_DIR)
204 return EXIT_FAILURE; 212 bb_show_usage();
205 d_flags = cp_mv_stat(last, &dest_stat); 213 /* "cp A B C... DIR" - target must be dir */
206 if (d_flags < 0) 214 } else /* argc == 2 */ {
207 return EXIT_FAILURE; 215 /* "cp A B" - only case where target can be not a dir */
216 s_flags = cp_mv_stat2(*argv, &source_stat,
217 (flags & FILEUTILS_DEREFERENCE) ? stat : lstat);
218 if (s_flags < 0) /* error other than ENOENT */
219 return EXIT_FAILURE;
220 d_flags = cp_mv_stat(last, &dest_stat);
221 if (d_flags < 0) /* error other than ENOENT */
222 return EXIT_FAILURE;
208 223
209 if (flags & FILEUTILS_NO_TARGET_DIR) { /* -T */ 224 if (flags & FILEUTILS_NO_TARGET_DIR) { /* -T */
210 if (!(s_flags & 2) && (d_flags & 2)) 225 if (!(s_flags & 2) && (d_flags & 2))
211 /* cp -T NOTDIR DIR */ 226 /* cp -T NOTDIR DIR */
212 bb_error_msg_and_die("'%s' is a directory", last); 227 bb_error_msg_and_die("'%s' is a directory", last);
213 } 228 }
214 229
215#if ENABLE_FEATURE_CP_LONG_OPTIONS 230#if ENABLE_FEATURE_CP_LONG_OPTIONS
216 //bb_error_msg("flags:%x FILEUTILS_RMDEST:%x OPT_parents:%x", 231 //bb_error_msg("flags:%x FILEUTILS_RMDEST:%x OPT_parents:%x",
217 // flags, FILEUTILS_RMDEST, OPT_parents); 232 // flags, FILEUTILS_RMDEST, OPT_parents);
218 if (flags & OPT_parents) { 233 if (flags & OPT_parents) {
219 if (!(d_flags & 2)) { 234 if (!(d_flags & 2)) {
220 bb_simple_error_msg_and_die("with --parents, the destination must be a directory"); 235 bb_simple_error_msg_and_die("with --parents, the destination must be a directory");
236 }
237 }
238 if (flags & FILEUTILS_RMDEST) {
239 flags |= FILEUTILS_FORCE;
221 } 240 }
222 }
223 if (flags & FILEUTILS_RMDEST) {
224 flags |= FILEUTILS_FORCE;
225 }
226#endif 241#endif
227 242
228 /* ...if neither is a directory... */ 243 /* ...if neither is a directory... */
229 if (!((s_flags | d_flags) & 2) 244 if (!((s_flags | d_flags) & 2)
230 /* ...or: recursing, the 1st is a directory, and the 2nd doesn't exist... */ 245 /* ...or: recursing, the 1st is a directory, and the 2nd doesn't exist... */
231 || ((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags) 246 || ((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags)
232 || (flags & FILEUTILS_NO_TARGET_DIR) 247 || (flags & FILEUTILS_NO_TARGET_DIR)
233 ) { 248 ) {
234 /* Do a simple copy */ 249 /* Do a simple copy */
235 dest = last; 250 dest = last;
236 goto DO_COPY; /* NB: argc==2 -> *++argv==last */ 251 goto DO_COPY; /* NB: argc==2 -> *++argv==last */
252 }
237 } 253 }
238 } else if (flags & FILEUTILS_NO_TARGET_DIR) {
239 bb_simple_error_msg_and_die("too many arguments");
240 } 254 }
255 /* else: last is DIR from "-t DIR" */
241 256
242 while (1) { 257 while (1) {
243#if ENABLE_FEATURE_CP_LONG_OPTIONS 258#if ENABLE_FEATURE_CP_LONG_OPTIONS
@@ -259,7 +274,7 @@ int cp_main(int argc, char **argv)
259 if (copy_file(*argv, dest, flags) < 0) { 274 if (copy_file(*argv, dest, flags) < 0) {
260 status = EXIT_FAILURE; 275 status = EXIT_FAILURE;
261 } 276 }
262 if (*++argv == last) { 277 if (!*++argv || *argv == last) {
263 /* possibly leaking dest... */ 278 /* possibly leaking dest... */
264 break; 279 break;
265 } 280 }
diff --git a/coreutils/cut.c b/coreutils/cut.c
index 5897d82b6..cc3c32576 100644
--- a/coreutils/cut.c
+++ b/coreutils/cut.c
@@ -29,6 +29,7 @@
29//usage: "\n -s Output only lines containing delimiter" 29//usage: "\n -s Output only lines containing delimiter"
30//usage: "\n -f LIST Print only these fields" 30//usage: "\n -f LIST Print only these fields"
31//usage: "\n -n Ignored" 31//usage: "\n -n Ignored"
32//(manpage:-n with -b: don't split multibyte characters)
32//usage: 33//usage:
33//usage:#define cut_example_usage 34//usage:#define cut_example_usage
34//usage: "$ echo \"Hello world\" | cut -f 1 -d ' '\n" 35//usage: "$ echo \"Hello world\" | cut -f 1 -d ' '\n"
diff --git a/coreutils/df.c b/coreutils/df.c
index debb86867..e8d4bc8f2 100644
--- a/coreutils/df.c
+++ b/coreutils/df.c
@@ -45,7 +45,7 @@
45//usage: IF_FEATURE_HUMAN_READABLE("mh") 45//usage: IF_FEATURE_HUMAN_READABLE("mh")
46//usage: "T" 46//usage: "T"
47//usage: IF_FEATURE_DF_FANCY("ai] [-B SIZE") 47//usage: IF_FEATURE_DF_FANCY("ai] [-B SIZE")
48//usage: "] [FILESYSTEM]..." 48//usage: "] [-t TYPE] [FILESYSTEM]..."
49//usage:#define df_full_usage "\n\n" 49//usage:#define df_full_usage "\n\n"
50//usage: "Print filesystem usage statistics\n" 50//usage: "Print filesystem usage statistics\n"
51//usage: "\n -P POSIX output format" 51//usage: "\n -P POSIX output format"
@@ -55,6 +55,7 @@
55//usage: "\n -h Human readable (e.g. 1K 243M 2G)" 55//usage: "\n -h Human readable (e.g. 1K 243M 2G)"
56//usage: ) 56//usage: )
57//usage: "\n -T Print filesystem type" 57//usage: "\n -T Print filesystem type"
58//usage: "\n -t TYPE Print only mounts of this type"
58//usage: IF_FEATURE_DF_FANCY( 59//usage: IF_FEATURE_DF_FANCY(
59//usage: "\n -a Show all filesystems" 60//usage: "\n -a Show all filesystems"
60//usage: "\n -i Inodes" 61//usage: "\n -i Inodes"
@@ -97,24 +98,31 @@ int df_main(int argc UNUSED_PARAM, char **argv)
97 FILE *mount_table; 98 FILE *mount_table;
98 struct mntent *mount_entry; 99 struct mntent *mount_entry;
99 struct statvfs s; 100 struct statvfs s;
100
101 enum { 101 enum {
102 OPT_KILO = (1 << 0), 102 OPT_KILO = (1 << 0),
103 OPT_POSIX = (1 << 1), 103 OPT_POSIX = (1 << 1),
104 OPT_FSTYPE = (1 << 2), 104 OPT_FSTYPE = (1 << 2),
105 OPT_ALL = (1 << 3) * ENABLE_FEATURE_DF_FANCY, 105 OPT_t = (1 << 3),
106 OPT_INODE = (1 << 4) * ENABLE_FEATURE_DF_FANCY, 106 OPT_ALL = (1 << 4) * ENABLE_FEATURE_DF_FANCY,
107 OPT_BSIZE = (1 << 5) * ENABLE_FEATURE_DF_FANCY, 107 OPT_INODE = (1 << 5) * ENABLE_FEATURE_DF_FANCY,
108 OPT_HUMAN = (1 << (3 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE, 108 OPT_BSIZE = (1 << 6) * ENABLE_FEATURE_DF_FANCY,
109 OPT_MEGA = (1 << (4 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE, 109 OPT_HUMAN = (1 << (4 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE,
110 OPT_MEGA = (1 << (5 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE,
110 }; 111 };
111 const char *disp_units_hdr = NULL; 112 const char *disp_units_hdr = NULL;
112 char *chp; 113 char *chp, *opt_t;
113 114
114 init_unicode(); 115 init_unicode();
115 116
117 /* From the manpage of df from coreutils-6.10:
118 * Disk space is shown in 1K blocks by default, unless the environment
119 * variable POSIXLY_CORRECT is set, in which case 512-byte blocks are used.
120 */
121 if (getenv("POSIXLY_CORRECT")) /* TODO - a new libbb function? */
122 df_disp_hr = 512;
123
116 opt = getopt32(argv, "^" 124 opt = getopt32(argv, "^"
117 "kPT" 125 "kPTt:"
118 IF_FEATURE_DF_FANCY("aiB:") 126 IF_FEATURE_DF_FANCY("aiB:")
119 IF_FEATURE_HUMAN_READABLE("hm") 127 IF_FEATURE_HUMAN_READABLE("hm")
120 "\0" 128 "\0"
@@ -123,6 +131,7 @@ int df_main(int argc UNUSED_PARAM, char **argv)
123#elif ENABLE_FEATURE_HUMAN_READABLE 131#elif ENABLE_FEATURE_HUMAN_READABLE
124 "k-m:m-k" 132 "k-m:m-k"
125#endif 133#endif
134 , &opt_t
126 IF_FEATURE_DF_FANCY(, &chp) 135 IF_FEATURE_DF_FANCY(, &chp)
127 ); 136 );
128 if (opt & OPT_MEGA) 137 if (opt & OPT_MEGA)
@@ -142,13 +151,6 @@ int df_main(int argc UNUSED_PARAM, char **argv)
142 got_it: ; 151 got_it: ;
143 } 152 }
144 153
145 /* From the manpage of df from coreutils-6.10:
146 * Disk space is shown in 1K blocks by default, unless the environment
147 * variable POSIXLY_CORRECT is set, in which case 512-byte blocks are used.
148 */
149 if (getenv("POSIXLY_CORRECT")) /* TODO - a new libbb function? */
150 df_disp_hr = 512;
151
152 if (opt & OPT_HUMAN) { 154 if (opt & OPT_HUMAN) {
153 df_disp_hr = 0; 155 df_disp_hr = 0;
154 disp_units_hdr = " Size"; 156 disp_units_hdr = " Size";
@@ -214,6 +216,11 @@ int df_main(int argc UNUSED_PARAM, char **argv)
214 mount_point = mount_entry->mnt_dir; 216 mount_point = mount_entry->mnt_dir;
215 fs_type = mount_entry->mnt_type; 217 fs_type = mount_entry->mnt_type;
216 218
219 if (opt & OPT_t) {
220 if (strcmp(fs_type, opt_t) != 0)
221 continue;
222 }
223
217 if (statvfs(mount_point, &s) != 0) { 224 if (statvfs(mount_point, &s) != 0) {
218 bb_simple_perror_msg(mount_point); 225 bb_simple_perror_msg(mount_point);
219 goto set_error; 226 goto set_error;
diff --git a/coreutils/du.c b/coreutils/du.c
index 247a08c95..092647468 100644
--- a/coreutils/du.c
+++ b/coreutils/du.c
@@ -42,6 +42,7 @@
42//usage:#define du_full_usage "\n\n" 42//usage:#define du_full_usage "\n\n"
43//usage: "Summarize disk space used for FILEs (or directories)\n" 43//usage: "Summarize disk space used for FILEs (or directories)\n"
44//usage: "\n -a Show file sizes too" 44//usage: "\n -a Show file sizes too"
45//usage: "\n -b Apparent size (including holes)"
45//usage: "\n -L Follow all symlinks" 46//usage: "\n -L Follow all symlinks"
46//usage: "\n -H Follow symlinks on command line" 47//usage: "\n -H Follow symlinks on command line"
47//usage: "\n -d N Limit output to directories (and files with -a) of depth < N" 48//usage: "\n -d N Limit output to directories (and files with -a) of depth < N"
@@ -84,8 +85,9 @@ enum {
84 OPT_d_maxdepth = (1 << 6), 85 OPT_d_maxdepth = (1 << 6),
85 OPT_l_hardlinks = (1 << 7), 86 OPT_l_hardlinks = (1 << 7),
86 OPT_c_total = (1 << 8), 87 OPT_c_total = (1 << 8),
87 OPT_h_for_humans = (1 << 9), 88 OPT_b = (1 << 9),
88 OPT_m_mbytes = (1 << 10), 89 OPT_h_for_humans = (1 << 10),
90 OPT_m_mbytes = (1 << 11),
89}; 91};
90 92
91struct globals { 93struct globals {
@@ -109,7 +111,7 @@ static void print(unsigned long long size, const char *filename)
109 /* TODO - May not want to defer error checking here. */ 111 /* TODO - May not want to defer error checking here. */
110#if ENABLE_FEATURE_HUMAN_READABLE 112#if ENABLE_FEATURE_HUMAN_READABLE
111# if ENABLE_DESKTOP 113# if ENABLE_DESKTOP
112 /* ~30 bytes of code for extra comtat: 114 /* ~30 bytes of code for extra compat:
113 * coreutils' du rounds sizes up: 115 * coreutils' du rounds sizes up:
114 * for example, 1025k file is shown as "2" by du -m. 116 * for example, 1025k file is shown as "2" by du -m.
115 * We round to nearest if human-readable [too hard to fix], 117 * We round to nearest if human-readable [too hard to fix],
@@ -124,12 +126,16 @@ static void print(unsigned long long size, const char *filename)
124 * If G.disp_unit == 0, show one fractional 126 * If G.disp_unit == 0, show one fractional
125 * and use suffixes 127 * and use suffixes
126 */ 128 */
127 make_human_readable_str(size, 512, G.disp_unit), 129 make_human_readable_str(size, (option_mask32 & OPT_b) ? 1 : 512, G.disp_unit),
128 filename); 130 filename);
129#else 131#else
130 if (G.disp_k) { 132 if (G.disp_k) {
131 size++; 133 if (!(option_mask32 & OPT_b)) {
132 size >>= 1; 134 size++;
135 size >>= 1;
136 } else {
137 size >>= 10;
138 }
133 } 139 }
134 printf("%"LL_FMT"u\t%s\n", size, filename); 140 printf("%"LL_FMT"u\t%s\n", size, filename);
135#endif 141#endif
@@ -155,7 +161,7 @@ static unsigned long long du(const char *filename)
155 } 161 }
156 } 162 }
157 163
158 sum = statbuf.st_blocks; 164 sum = ((option_mask32 & OPT_b) ? statbuf.st_size : statbuf.st_blocks);
159 165
160 if (S_ISLNK(statbuf.st_mode)) { 166 if (S_ISLNK(statbuf.st_mode)) {
161 if (G.slink_depth > G.du_depth) { /* -H or -L */ 167 if (G.slink_depth > G.du_depth) { /* -H or -L */
@@ -164,7 +170,7 @@ static unsigned long long du(const char *filename)
164 G.status = EXIT_FAILURE; 170 G.status = EXIT_FAILURE;
165 return 0; 171 return 0;
166 } 172 }
167 sum = statbuf.st_blocks; 173 sum = ((option_mask32 & OPT_b) ? statbuf.st_size : statbuf.st_blocks);
168 if (G.slink_depth == 1) { 174 if (G.slink_depth == 1) {
169 /* Convert -H to -L */ 175 /* Convert -H to -L */
170 G.slink_depth = INT_MAX; 176 G.slink_depth = INT_MAX;
@@ -241,11 +247,14 @@ int du_main(int argc UNUSED_PARAM, char **argv)
241 */ 247 */
242#if ENABLE_FEATURE_HUMAN_READABLE 248#if ENABLE_FEATURE_HUMAN_READABLE
243 opt = getopt32(argv, "^" 249 opt = getopt32(argv, "^"
244 "aHkLsxd:+lchm" 250 "aHkLsxd:+lcbhm"
245 "\0" "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s", 251 "\0" "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s",
246 &G.max_print_depth 252 &G.max_print_depth
247 ); 253 );
248 argv += optind; 254 argv += optind;
255 if (opt & OPT_b) {
256 G.disp_unit = 1;
257 }
249 if (opt & OPT_h_for_humans) { 258 if (opt & OPT_h_for_humans) {
250 G.disp_unit = 0; 259 G.disp_unit = 0;
251 } 260 }
@@ -257,16 +266,16 @@ int du_main(int argc UNUSED_PARAM, char **argv)
257 } 266 }
258#else 267#else
259 opt = getopt32(argv, "^" 268 opt = getopt32(argv, "^"
260 "aHkLsxd:+lc" 269 "aHkLsxd:+lcb"
261 "\0" "H-L:L-H:s-d:d-s", 270 "\0" "H-L:L-H:s-d:d-s",
262 &G.max_print_depth 271 &G.max_print_depth
263 ); 272 );
264 argv += optind; 273 argv += optind;
265#if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K 274# if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
266 if (opt & OPT_k_kbytes) { 275 if (opt & OPT_k_kbytes) {
267 G.disp_k = 1; 276 G.disp_k = 1;
268 } 277 }
269#endif 278# endif
270#endif 279#endif
271 if (opt & OPT_H_follow_links) { 280 if (opt & OPT_H_follow_links) {
272 G.slink_depth = 1; 281 G.slink_depth = 1;
diff --git a/coreutils/echo.c b/coreutils/echo.c
index aab177cee..82f0358b6 100644
--- a/coreutils/echo.c
+++ b/coreutils/echo.c
@@ -43,10 +43,10 @@
43//usage:#define echo_trivial_usage 43//usage:#define echo_trivial_usage
44//usage: IF_FEATURE_FANCY_ECHO("[-neE] ") "[ARG]..." 44//usage: IF_FEATURE_FANCY_ECHO("[-neE] ") "[ARG]..."
45//usage:#define echo_full_usage "\n\n" 45//usage:#define echo_full_usage "\n\n"
46//usage: "Print the specified ARGs to stdout" 46//usage: "Print ARGs to stdout"
47//usage: IF_FEATURE_FANCY_ECHO( "\n" 47//usage: IF_FEATURE_FANCY_ECHO( "\n"
48//usage: "\n -n Suppress trailing newline" 48//usage: "\n -n No trailing newline"
49//usage: "\n -e Interpret backslash escapes (i.e., \\t=tab)" 49//usage: "\n -e Interpret backslash escapes (\\t=tab etc)"
50//usage: "\n -E Don't interpret backslash escapes (default)" 50//usage: "\n -E Don't interpret backslash escapes (default)"
51//usage: ) 51//usage: )
52//usage: 52//usage:
diff --git a/coreutils/env.c b/coreutils/env.c
index c37c0c2df..a0ea4dd27 100644
--- a/coreutils/env.c
+++ b/coreutils/env.c
@@ -39,12 +39,15 @@
39/* http://www.opengroup.org/onlinepubs/007904975/utilities/env.html */ 39/* http://www.opengroup.org/onlinepubs/007904975/utilities/env.html */
40 40
41//usage:#define env_trivial_usage 41//usage:#define env_trivial_usage
42//usage: "[-iu] [-] [name=value]... [PROG ARGS]" 42//usage: "[-i0] [-u NAME]... [-] [NAME=VALUE]... [PROG ARGS]"
43// The "-" can occur only once (unlike, say, -i): it terminates option processing,
44// so if it is followed by another "-" arg (or any option-looking arg),
45// that arg will be taken as PROG (or even as NAME=VALUE, example: "-z=QWE").
43//usage:#define env_full_usage "\n\n" 46//usage:#define env_full_usage "\n\n"
44//usage: "Print the current environment or run PROG after setting up\n" 47//usage: "Print current environment or run PROG after setting up environment\n"
45//usage: "the specified environment\n" 48//usage: "\n -, -i Start with empty environment"
46//usage: "\n -, -i Start with an empty environment" 49//usage: "\n -0 NUL terminated output"
47//usage: "\n -u Remove variable from the environment" 50//usage: "\n -u NAME Remove variable from environment"
48 51
49#include "libbb.h" 52#include "libbb.h"
50 53
@@ -54,8 +57,9 @@ int env_main(int argc UNUSED_PARAM, char **argv)
54 unsigned opts; 57 unsigned opts;
55 llist_t *unset_env = NULL; 58 llist_t *unset_env = NULL;
56 59
57 opts = getopt32long(argv, "+iu:*", 60 opts = getopt32long(argv, "+i0u:*",
58 "ignore-environment\0" No_argument "i" 61 "ignore-environment\0" No_argument "i"
62 "null\0" No_argument "0"
59 "unset\0" Required_argument "u" 63 "unset\0" Required_argument "u"
60 , &unset_env 64 , &unset_env
61 ); 65 );
@@ -90,8 +94,9 @@ int env_main(int argc UNUSED_PARAM, char **argv)
90 94
91 if (environ) { /* clearenv() may set environ == NULL! */ 95 if (environ) { /* clearenv() may set environ == NULL! */
92 char **ep; 96 char **ep;
97 opts = (opts & 2) ? 0 : '\n';
93 for (ep = environ; *ep; ep++) { 98 for (ep = environ; *ep; ep++) {
94 puts(*ep); 99 printf("%s%c", *ep, opts);
95 } 100 }
96 } 101 }
97 102
diff --git a/coreutils/expr.c b/coreutils/expr.c
index c11505d13..b896acd44 100644
--- a/coreutils/expr.c
+++ b/coreutils/expr.c
@@ -45,7 +45,7 @@
45//usage:#define expr_trivial_usage 45//usage:#define expr_trivial_usage
46//usage: "EXPRESSION" 46//usage: "EXPRESSION"
47//usage:#define expr_full_usage "\n\n" 47//usage:#define expr_full_usage "\n\n"
48//usage: "Print the value of EXPRESSION to stdout\n" 48//usage: "Print the value of EXPRESSION\n"
49//usage: "\n" 49//usage: "\n"
50//usage: "EXPRESSION may be:\n" 50//usage: "EXPRESSION may be:\n"
51//usage: " ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n" 51//usage: " ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n"
@@ -63,7 +63,7 @@
63//usage: " ARG1 % ARG2\n" 63//usage: " ARG1 % ARG2\n"
64//usage: " STRING : REGEXP Anchored pattern match of REGEXP in STRING\n" 64//usage: " STRING : REGEXP Anchored pattern match of REGEXP in STRING\n"
65//usage: " match STRING REGEXP Same as STRING : REGEXP\n" 65//usage: " match STRING REGEXP Same as STRING : REGEXP\n"
66//usage: " substr STRING POS LENGTH Substring of STRING, POS counted from 1\n" 66//usage: " substr STRING POS LEN Substring of STRING, POS counts from 1\n"
67//usage: " index STRING CHARS Index in STRING where any CHARS is found, or 0\n" 67//usage: " index STRING CHARS Index in STRING where any CHARS is found, or 0\n"
68//usage: " length STRING Length of STRING\n" 68//usage: " length STRING Length of STRING\n"
69//usage: " quote TOKEN Interpret TOKEN as a string, even if\n" 69//usage: " quote TOKEN Interpret TOKEN as a string, even if\n"
diff --git a/coreutils/id.c b/coreutils/id.c
index f453a87ae..18bda3c55 100644
--- a/coreutils/id.c
+++ b/coreutils/id.c
@@ -52,7 +52,7 @@
52//usage:#define groups_trivial_usage 52//usage:#define groups_trivial_usage
53//usage: "[USER]" 53//usage: "[USER]"
54//usage:#define groups_full_usage "\n\n" 54//usage:#define groups_full_usage "\n\n"
55//usage: "Print the group memberships of USER or for the current process" 55//usage: "Print the groups USER is in"
56//usage: 56//usage:
57//usage:#define groups_example_usage 57//usage:#define groups_example_usage
58//usage: "$ groups\n" 58//usage: "$ groups\n"
diff --git a/coreutils/mv.c b/coreutils/mv.c
index f5ed9fcfc..fd2422683 100644
--- a/coreutils/mv.c
+++ b/coreutils/mv.c
@@ -23,13 +23,15 @@
23//kbuild:lib-$(CONFIG_MV) += mv.o 23//kbuild:lib-$(CONFIG_MV) += mv.o
24 24
25//usage:#define mv_trivial_usage 25//usage:#define mv_trivial_usage
26//usage: "[-fin] SOURCE DEST\n" 26//usage: "[-finT] SOURCE DEST\n"
27//usage: "or: mv [-fin] SOURCE... DIRECTORY" 27//usage: "or: mv [-fin] SOURCE... { -t DIRECTORY | DIRECTORY }"
28//usage:#define mv_full_usage "\n\n" 28//usage:#define mv_full_usage "\n\n"
29//usage: "Rename SOURCE to DEST, or move SOURCEs to DIRECTORY\n" 29//usage: "Rename SOURCE to DEST, or move SOURCEs to DIRECTORY\n"
30//usage: "\n -f Don't prompt before overwriting" 30//usage: "\n -f Don't prompt before overwriting"
31//usage: "\n -i Interactive, prompt before overwrite" 31//usage: "\n -i Interactive, prompt before overwrite"
32//usage: "\n -n Don't overwrite an existing file" 32//usage: "\n -n Don't overwrite an existing file"
33//usage: "\n -T Refuse to move if DEST is a directory"
34//usage: "\n -t DIR Move all SOURCEs into DIR"
33//usage: 35//usage:
34//usage:#define mv_example_usage 36//usage:#define mv_example_usage
35//usage: "$ mv /tmp/foo /bin/bar\n" 37//usage: "$ mv /tmp/foo /bin/bar\n"
@@ -40,7 +42,7 @@
40int mv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 42int mv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
41int mv_main(int argc, char **argv) 43int mv_main(int argc, char **argv)
42{ 44{
43 struct stat dest_stat; 45 struct stat statbuf;
44 const char *last; 46 const char *last;
45 const char *dest; 47 const char *dest;
46 unsigned flags; 48 unsigned flags;
@@ -51,41 +53,66 @@ int mv_main(int argc, char **argv)
51#define OPT_FORCE (1 << 0) 53#define OPT_FORCE (1 << 0)
52#define OPT_INTERACTIVE (1 << 1) 54#define OPT_INTERACTIVE (1 << 1)
53#define OPT_NOCLOBBER (1 << 2) 55#define OPT_NOCLOBBER (1 << 2)
54#define OPT_VERBOSE ((1 << 3) * ENABLE_FEATURE_VERBOSE) 56#define OPT_DESTNOTDIR (1 << 3)
55 /* Need at least two arguments. 57#define OPT_DESTDIR (1 << 4)
56 * If more than one of -f, -i, -n is specified , only the final one 58#define OPT_VERBOSE ((1 << 5) * ENABLE_FEATURE_VERBOSE)
57 * takes effect (it unsets previous options).
58 */
59 flags = getopt32long(argv, "^" 59 flags = getopt32long(argv, "^"
60 "finv" 60 "finTt:v"
61 "\0" 61 "\0"
62 "-2:f-in:i-fn:n-fi", 62 /* At least one argument. (Usually two+, but -t DIR can have only one) */
63 "-1"
64 /* only the final one of -f, -i, -n takes effect */
65 ":f-in:i-fn:n-fi"
66 /* -t and -T don't mix */
67 ":t--T:T--t",
63 "interactive\0" No_argument "i" 68 "interactive\0" No_argument "i"
64 "force\0" No_argument "f" 69 "force\0" No_argument "f"
65 "no-clobber\0" No_argument "n" 70 "no-clobber\0" No_argument "n"
71 "no-target-directory\0" No_argument "T"
72 "target-directory\0" Required_argument "t"
66 IF_FEATURE_VERBOSE( 73 IF_FEATURE_VERBOSE(
67 "verbose\0" No_argument "v" 74 "verbose\0" No_argument "v",
75 &last
68 ) 76 )
69 ); 77 );
70 argc -= optind; 78 argc -= optind;
71 argv += optind; 79 argv += optind;
72 last = argv[argc - 1];
73 80
74 if (argc == 2) { 81 if (!(flags & OPT_DESTDIR)) {
75 dest_exists = cp_mv_stat(last, &dest_stat); 82 last = argv[argc - 1];
76 if (dest_exists < 0) { 83 if (argc < 2)
77 return EXIT_FAILURE; 84 bb_show_usage();
78 } 85 if (argc != 2) {
79 86 if (flags & OPT_DESTNOTDIR)
80 if (!(dest_exists & 2)) { /* last is not a directory */ 87 bb_show_usage();
81 dest = last; 88 /* "mv A B C... DIR" - target must be dir */
82 goto DO_MOVE; 89 } else /* argc == 2 */ {
90 /* "mv A B" - only case where target can be not a dir */
91 dest_exists = cp_mv_stat(last, &statbuf);
92 if (dest_exists < 0) { /* error other than ENOENT */
93 return EXIT_FAILURE;
94 }
95 if (!(dest_exists & 2)) {
96 /* last is not a directory */
97 dest = last;
98 goto DO_MOVE;
99 }
100 /* last is a directory */
101 if (flags & OPT_DESTNOTDIR) {
102 if (stat(argv[0], &statbuf) == 0 && !S_ISDIR(statbuf.st_mode))
103 bb_error_msg_and_die("'%s' is a directory", last);
104 /* "mv -T DIR1 DIR2" is allowed (renames a dir) */
105 dest = last;
106 goto DO_MOVE;
107 }
108 /* else: fall through into "do { move SRC to DIR/SRC } while" loop */
83 } 109 }
84 } 110 }
111 /* else: last is DIR from "-t DIR" */
85 112
86 do { 113 do {
87 dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); 114 dest = concat_path_file(last, bb_get_last_path_component_strip(*argv));
88 dest_exists = cp_mv_stat(dest, &dest_stat); 115 dest_exists = cp_mv_stat(dest, &statbuf);
89 if (dest_exists < 0) { 116 if (dest_exists < 0) {
90 goto RET_1; 117 goto RET_1;
91 } 118 }
@@ -108,11 +135,10 @@ int mv_main(int argc, char **argv)
108 } 135 }
109 136
110 if (rename(*argv, dest) < 0) { 137 if (rename(*argv, dest) < 0) {
111 struct stat source_stat;
112 int source_exists; 138 int source_exists;
113 139
114 if (errno != EXDEV 140 if (errno != EXDEV
115 || (source_exists = cp_mv_stat2(*argv, &source_stat, lstat)) < 1 141 || (source_exists = cp_mv_stat2(*argv, &statbuf, lstat)) < 1
116 ) { 142 ) {
117 bb_perror_msg("can't rename '%s'", *argv); 143 bb_perror_msg("can't rename '%s'", *argv);
118 } else { 144 } else {
@@ -159,7 +185,7 @@ int mv_main(int argc, char **argv)
159 if (dest != last) { 185 if (dest != last) {
160 free((void *) dest); 186 free((void *) dest);
161 } 187 }
162 } while (*++argv != last); 188 } while (*++argv && *argv != last);
163 189
164 return status; 190 return status;
165} 191}
diff --git a/coreutils/nproc.c b/coreutils/nproc.c
index 7025765c5..b345255a7 100644
--- a/coreutils/nproc.c
+++ b/coreutils/nproc.c
@@ -14,7 +14,7 @@
14//kbuild:lib-$(CONFIG_NPROC) += nproc.o 14//kbuild:lib-$(CONFIG_NPROC) += nproc.o
15 15
16//usage:#define nproc_trivial_usage 16//usage:#define nproc_trivial_usage
17//usage: ""IF_LONG_OPTS("--all --ignore=N") 17//usage: ""IF_LONG_OPTS("[--all] [--ignore=N]")
18//usage:#define nproc_full_usage "\n\n" 18//usage:#define nproc_full_usage "\n\n"
19//usage: "Print number of available CPUs" 19//usage: "Print number of available CPUs"
20//usage: IF_LONG_OPTS( 20//usage: IF_LONG_OPTS(
@@ -49,7 +49,7 @@ int nproc_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
49 if (cpuid && isdigit(cpuid[strlen(cpuid) - 1])) 49 if (cpuid && isdigit(cpuid[strlen(cpuid) - 1]))
50 count++; 50 count++;
51 } 51 }
52 closedir(cpusd); 52 IF_FEATURE_CLEAN_UP(closedir(cpusd);)
53 } 53 }
54 } else 54 } else
55#endif 55#endif
diff --git a/coreutils/shred.c b/coreutils/shred.c
index 0b11b9491..794d7b815 100644
--- a/coreutils/shred.c
+++ b/coreutils/shred.c
@@ -15,14 +15,15 @@
15//kbuild:lib-$(CONFIG_SHRED) += shred.o 15//kbuild:lib-$(CONFIG_SHRED) += shred.o
16 16
17//usage:#define shred_trivial_usage 17//usage:#define shred_trivial_usage
18//usage: "FILE..." 18//usage: "[-fuz] [-n N] [-s SIZE] FILE..."
19//usage:#define shred_full_usage "\n\n" 19//usage:#define shred_full_usage "\n\n"
20//usage: "Overwrite/delete FILEs\n" 20//usage: "Overwrite/delete FILEs\n"
21//usage: "\n -f Chmod to ensure writability" 21//usage: "\n -f Chmod to ensure writability"
22//usage: "\n -s SIZE Size to write"
22//usage: "\n -n N Overwrite N times (default 3)" 23//usage: "\n -n N Overwrite N times (default 3)"
23//usage: "\n -z Final overwrite with zeros" 24//usage: "\n -z Final overwrite with zeros"
24//usage: "\n -u Remove file" 25//usage: "\n -u Remove file"
25//-x and -v are accepted but have no effect 26//-x (exact: don't round up to 4k) and -v (verbose) are accepted but have no effect
26 27
27/* shred (GNU coreutils) 8.25: 28/* shred (GNU coreutils) 8.25:
28-f, --force change permissions to allow writing if necessary 29-f, --force change permissions to allow writing if necessary
@@ -41,6 +42,7 @@
41int shred_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 42int shred_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
42int shred_main(int argc UNUSED_PARAM, char **argv) 43int shred_main(int argc UNUSED_PARAM, char **argv)
43{ 44{
45 char *opt_s;
44 int rand_fd = rand_fd; /* for compiler */ 46 int rand_fd = rand_fd; /* for compiler */
45 int zero_fd; 47 int zero_fd;
46 unsigned num_iter = 3; 48 unsigned num_iter = 3;
@@ -52,18 +54,16 @@ int shred_main(int argc UNUSED_PARAM, char **argv)
52 OPT_n = (1 << 3), 54 OPT_n = (1 << 3),
53 OPT_v = (1 << 4), 55 OPT_v = (1 << 4),
54 OPT_x = (1 << 5), 56 OPT_x = (1 << 5),
57 OPT_s = (1 << 6),
55 }; 58 };
56 59
57 opt = getopt32(argv, "fuzn:+vx", &num_iter); 60 opt = getopt32(argv, "^" "fuzn:+vxs:" "\0" "-1"/*min 1 arg*/, &num_iter, &opt_s);
58 argv += optind; 61 argv += optind;
59 62
60 zero_fd = MINGW_SPECIAL(xopen)("/dev/zero", O_RDONLY); 63 zero_fd = MINGW_SPECIAL(xopen)("/dev/zero", O_RDONLY);
61 if (num_iter != 0) 64 if (num_iter != 0)
62 rand_fd = MINGW_SPECIAL(xopen)("/dev/urandom", O_RDONLY); 65 rand_fd = MINGW_SPECIAL(xopen)("/dev/urandom", O_RDONLY);
63 66
64 if (!*argv)
65 bb_show_usage();
66
67 for (;;) { 67 for (;;) {
68 struct stat sb; 68 struct stat sb;
69 const char *fname; 69 const char *fname;
@@ -85,6 +85,11 @@ int shred_main(int argc UNUSED_PARAM, char **argv)
85 if (fstat(fd, &sb) == 0 && sb.st_size > 0) { 85 if (fstat(fd, &sb) == 0 && sb.st_size > 0) {
86 off_t size = sb.st_size; 86 off_t size = sb.st_size;
87 87
88 if (opt & OPT_s) {
89 size = BB_STRTOOFF(opt_s, NULL, 0); /* accepts oct/hex */
90 if (errno || size < 0) bb_show_usage();
91 }
92
88 for (i = 0; i < num_iter; i++) { 93 for (i = 0; i < num_iter; i++) {
89 bb_copyfd_size(rand_fd, fd, size); 94 bb_copyfd_size(rand_fd, fd, size);
90 fdatasync(fd); 95 fdatasync(fd);
@@ -94,18 +99,18 @@ int shred_main(int argc UNUSED_PARAM, char **argv)
94 bb_copyfd_size(zero_fd, fd, size); 99 bb_copyfd_size(zero_fd, fd, size);
95 fdatasync(fd); 100 fdatasync(fd);
96 } 101 }
97 if (opt & OPT_u) { 102 }
98 ftruncate(fd, 0); 103 if (opt & OPT_u) {
104 ftruncate(fd, 0);
99#if ENABLE_PLATFORM_MINGW32 105#if ENABLE_PLATFORM_MINGW32
100 xclose(fd); 106 xclose(fd);
101#endif 107#endif
102 xunlink(fname); 108 xunlink(fname);
103 } 109 }
104#if ENABLE_PLATFORM_MINGW32 110#if ENABLE_PLATFORM_MINGW32
105 else 111 else
106#endif 112#endif
107 xclose(fd); 113 xclose(fd);
108 }
109 } 114 }
110 115
111 return EXIT_SUCCESS; 116 return EXIT_SUCCESS;
diff --git a/coreutils/sort.c b/coreutils/sort.c
index 6c4e3038c..32a06e40a 100644
--- a/coreutils/sort.c
+++ b/coreutils/sort.c
@@ -67,7 +67,7 @@
67//usage: "\n -r Reverse sort order" 67//usage: "\n -r Reverse sort order"
68//usage: "\n -s Stable (don't sort ties alphabetically)" 68//usage: "\n -s Stable (don't sort ties alphabetically)"
69//usage: "\n -u Suppress duplicate lines" 69//usage: "\n -u Suppress duplicate lines"
70//usage: "\n -z Lines are terminated by NUL, not newline" 70//usage: "\n -z NUL terminated input and output"
71///////: "\n -m Ignored for GNU compatibility" 71///////: "\n -m Ignored for GNU compatibility"
72///////: "\n -S BUFSZ Ignored for GNU compatibility" 72///////: "\n -S BUFSZ Ignored for GNU compatibility"
73///////: "\n -T TMPDIR Ignored for GNU compatibility" 73///////: "\n -T TMPDIR Ignored for GNU compatibility"
diff --git a/coreutils/touch.c b/coreutils/touch.c
index 2b225dd16..ec12eb7cf 100644
--- a/coreutils/touch.c
+++ b/coreutils/touch.c
@@ -31,7 +31,7 @@
31//kbuild:lib-$(CONFIG_TOUCH) += touch.o 31//kbuild:lib-$(CONFIG_TOUCH) += touch.o
32 32
33//usage:#define touch_trivial_usage 33//usage:#define touch_trivial_usage
34//usage: "[-c" IF_FEATURE_TOUCH_SUSV3("am") "]" 34//usage: "[-ch" IF_FEATURE_TOUCH_SUSV3("am") "]"
35//usage: IF_FEATURE_TOUCH_SUSV3(" [-d DATE] [-t DATE] [-r FILE]") 35//usage: IF_FEATURE_TOUCH_SUSV3(" [-d DATE] [-t DATE] [-r FILE]")
36//usage: " FILE..." 36//usage: " FILE..."
37//usage:#define touch_full_usage "\n\n" 37//usage:#define touch_full_usage "\n\n"
diff --git a/coreutils/tty.c b/coreutils/tty.c
index ff6f2bb3b..e448c27ec 100644
--- a/coreutils/tty.c
+++ b/coreutils/tty.c
@@ -21,7 +21,7 @@
21/* http://www.opengroup.org/onlinepubs/9699919799/utilities/tty.html */ 21/* http://www.opengroup.org/onlinepubs/9699919799/utilities/tty.html */
22 22
23//usage:#define tty_trivial_usage 23//usage:#define tty_trivial_usage
24//usage: "" 24//usage: "" IF_INCLUDE_SUSv2("[-s]")
25//usage:#define tty_full_usage "\n\n" 25//usage:#define tty_full_usage "\n\n"
26//usage: "Print file name of stdin's terminal" 26//usage: "Print file name of stdin's terminal"
27//usage: IF_INCLUDE_SUSv2( "\n" 27//usage: IF_INCLUDE_SUSv2( "\n"
diff --git a/coreutils/uname.c b/coreutils/uname.c
index 2a1602b4c..da785ab4c 100644
--- a/coreutils/uname.c
+++ b/coreutils/uname.c
@@ -79,13 +79,13 @@
79//usage:#define uname_full_usage "\n\n" 79//usage:#define uname_full_usage "\n\n"
80//usage: "Print system information\n" 80//usage: "Print system information\n"
81//usage: "\n -a Print all" 81//usage: "\n -a Print all"
82//usage: "\n -m The machine (hardware) type" 82//usage: "\n -m Machine (hardware) type"
83//usage: "\n -n Hostname" 83//usage: "\n -n Hostname"
84//usage: "\n -r Kernel release" 84//usage: "\n -r Kernel release"
85//usage: "\n -s Kernel name (default)" 85//usage: "\n -s Kernel name (default)"
86//usage: "\n -p Processor type" 86//usage: "\n -p Processor type"
87//usage: "\n -v Kernel version" 87//usage: "\n -v Kernel version"
88//usage: "\n -i The hardware platform" 88//usage: "\n -i Hardware platform"
89//usage: "\n -o OS name" 89//usage: "\n -o OS name"
90//usage: 90//usage:
91//usage:#define uname_example_usage 91//usage:#define uname_example_usage
diff --git a/coreutils/uniq.c b/coreutils/uniq.c
index e1594286f..a3058ac07 100644
--- a/coreutils/uniq.c
+++ b/coreutils/uniq.c
@@ -20,13 +20,14 @@
20/* http://www.opengroup.org/onlinepubs/007904975/utilities/uniq.html */ 20/* http://www.opengroup.org/onlinepubs/007904975/utilities/uniq.html */
21 21
22//usage:#define uniq_trivial_usage 22//usage:#define uniq_trivial_usage
23//usage: "[-cdui] [-f,s,w N] [INPUT [OUTPUT]]" 23//usage: "[-cduiz] [-f,s,w N] [FILE [OUTFILE]]"
24//usage:#define uniq_full_usage "\n\n" 24//usage:#define uniq_full_usage "\n\n"
25//usage: "Discard duplicate lines\n" 25//usage: "Discard duplicate lines\n"
26//usage: "\n -c Prefix lines by the number of occurrences" 26//usage: "\n -c Prefix lines by the number of occurrences"
27//usage: "\n -d Only print duplicate lines" 27//usage: "\n -d Only print duplicate lines"
28//usage: "\n -u Only print unique lines" 28//usage: "\n -u Only print unique lines"
29//usage: "\n -i Ignore case" 29//usage: "\n -i Ignore case"
30//usage: "\n -z NUL terminated output"
30//usage: "\n -f N Skip first N fields" 31//usage: "\n -f N Skip first N fields"
31//usage: "\n -s N Skip first N chars (after any skipped fields)" 32//usage: "\n -s N Skip first N chars (after any skipped fields)"
32//usage: "\n -w N Compare N characters in line" 33//usage: "\n -w N Compare N characters in line"
@@ -45,23 +46,25 @@ int uniq_main(int argc UNUSED_PARAM, char **argv)
45 const char *input_filename; 46 const char *input_filename;
46 unsigned skip_fields, skip_chars, max_chars; 47 unsigned skip_fields, skip_chars, max_chars;
47 unsigned opt; 48 unsigned opt;
49 char eol;
48 char *cur_line; 50 char *cur_line;
49 const char *cur_compare; 51 const char *cur_compare;
50 52
51 enum { 53 enum {
52 OPT_c = 0x1, 54 OPT_c = 1 << 0,
53 OPT_d = 0x2, /* print only dups */ 55 OPT_d = 1 << 1, /* print only dups */
54 OPT_u = 0x4, /* print only uniq */ 56 OPT_u = 1 << 2, /* print only uniq */
55 OPT_f = 0x8, 57 OPT_f = 1 << 3,
56 OPT_s = 0x10, 58 OPT_s = 1 << 4,
57 OPT_w = 0x20, 59 OPT_w = 1 << 5,
58 OPT_i = 0x40, 60 OPT_i = 1 << 6,
61 OPT_z = 1 << 7,
59 }; 62 };
60 63
61 skip_fields = skip_chars = 0; 64 skip_fields = skip_chars = 0;
62 max_chars = INT_MAX; 65 max_chars = INT_MAX;
63 66
64 opt = getopt32(argv, "cduf:+s:+w:+i", &skip_fields, &skip_chars, &max_chars); 67 opt = getopt32(argv, "cduf:+s:+w:+iz", &skip_fields, &skip_chars, &max_chars);
65 argv += optind; 68 argv += optind;
66 69
67 input_filename = argv[0]; 70 input_filename = argv[0];
@@ -86,6 +89,7 @@ int uniq_main(int argc UNUSED_PARAM, char **argv)
86 } 89 }
87 90
88 cur_compare = cur_line = NULL; /* prime the pump */ 91 cur_compare = cur_line = NULL; /* prime the pump */
92 eol = (opt & OPT_z) ? 0 : '\n';
89 93
90 do { 94 do {
91 unsigned i; 95 unsigned i;
@@ -127,7 +131,7 @@ int uniq_main(int argc UNUSED_PARAM, char **argv)
127 /* %7lu matches GNU coreutils 6.9 */ 131 /* %7lu matches GNU coreutils 6.9 */
128 printf("%7lu ", dups + 1); 132 printf("%7lu ", dups + 1);
129 } 133 }
130 puts(old_line); 134 printf("%s%c", old_line, eol);
131 } 135 }
132 free(old_line); 136 free(old_line);
133 } 137 }
diff --git a/coreutils/uudecode.c b/coreutils/uudecode.c
index 02b037276..a607977e9 100644
--- a/coreutils/uudecode.c
+++ b/coreutils/uudecode.c
@@ -183,7 +183,7 @@ int uudecode_main(int argc UNUSED_PARAM, char **argv)
183//usage:#define base32_trivial_usage 183//usage:#define base32_trivial_usage
184//usage: "[-d] [-w COL] [FILE]" 184//usage: "[-d] [-w COL] [FILE]"
185//usage:#define base32_full_usage "\n\n" 185//usage:#define base32_full_usage "\n\n"
186//usage: "Base32 encode or decode FILE to standard output" 186//usage: "Base32 encode or decode FILE to standard output\n"
187//usage: "\n -d Decode data" 187//usage: "\n -d Decode data"
188//usage: "\n -w COL Wrap lines at COL (default 76, 0 disables)" 188//usage: "\n -w COL Wrap lines at COL (default 76, 0 disables)"
189////usage: "\n -i When decoding, ignore non-alphabet characters" 189////usage: "\n -i When decoding, ignore non-alphabet characters"
@@ -191,10 +191,12 @@ int uudecode_main(int argc UNUSED_PARAM, char **argv)
191//usage:#define base64_trivial_usage 191//usage:#define base64_trivial_usage
192//usage: "[-d] [-w COL] [FILE]" 192//usage: "[-d] [-w COL] [FILE]"
193//usage:#define base64_full_usage "\n\n" 193//usage:#define base64_full_usage "\n\n"
194//usage: "Base64 encode or decode FILE to standard output" 194//usage: "Base64 encode or decode FILE to standard output\n"
195//usage: "\n -d Decode data" 195//usage: "\n -d Decode data"
196//usage: "\n -w COL Wrap lines at COL (default 76, 0 disables)" 196//usage: "\n -w COL Wrap lines at COL (default 76, 0 disables)"
197////usage: "\n -i When decoding, ignore non-alphabet characters" 197///////: "\n -i When decoding, ignore non-alphabet characters"
198// -i is accepted but has no effect: currently, decode_base32/64() functions
199// (called via read_base64()) skip invalid chars unconditionally.
198 200
199// APPLET_ODDNAME:name main location suid_type help 201// APPLET_ODDNAME:name main location suid_type help
200//applet:IF_BASE32(APPLET_ODDNAME(base32, baseNUM, BB_DIR_BIN, BB_SUID_DROP, base32)) 202//applet:IF_BASE32(APPLET_ODDNAME(base32, baseNUM, BB_DIR_BIN, BB_SUID_DROP, base32))
@@ -272,7 +274,7 @@ int baseNUM_main(int argc UNUSED_PARAM, char **argv)
272 unsigned opts; 274 unsigned opts;
273 unsigned col = 76; 275 unsigned col = 76;
274 276
275 opts = getopt32(argv, "^" "dw:+" "\0" "?1"/* 1 arg max*/, &col); 277 opts = getopt32(argv, "^" "diw:+" "\0" "?1"/* 1 arg max*/, &col);
276 argv += optind; 278 argv += optind;
277 279
278 if (!argv[0]) 280 if (!argv[0])
diff --git a/coreutils/who.c b/coreutils/who.c
index be9c3ccca..3725d77f5 100644
--- a/coreutils/who.c
+++ b/coreutils/who.c
@@ -78,7 +78,7 @@
78// root pts/1 Mon13 3:24m 1:01 0.01s w 78// root pts/1 Mon13 3:24m 1:01 0.01s w
79 79
80//usage:#define who_trivial_usage 80//usage:#define who_trivial_usage
81//usage: "[-a]" 81//usage: "[-aH]"
82//usage:#define who_full_usage "\n\n" 82//usage:#define who_full_usage "\n\n"
83//usage: "Show who is logged on\n" 83//usage: "Show who is logged on\n"
84//usage: "\n -a Show all" 84//usage: "\n -a Show all"
diff --git a/coreutils/yes.c b/coreutils/yes.c
index 38ffff46c..64dfa500c 100644
--- a/coreutils/yes.c
+++ b/coreutils/yes.c
@@ -27,7 +27,7 @@
27//usage:#define yes_trivial_usage 27//usage:#define yes_trivial_usage
28//usage: "[STRING]" 28//usage: "[STRING]"
29//usage:#define yes_full_usage "\n\n" 29//usage:#define yes_full_usage "\n\n"
30//usage: "Repeatedly output a line with STRING, or 'y'" 30//usage: "Repeatedly print a line with STRING, or 'y'"
31 31
32#include "libbb.h" 32#include "libbb.h"
33 33
diff --git a/docs/busybox_footer.pod b/docs/busybox_footer.pod
index 92748eb72..0f4810bd3 100644
--- a/docs/busybox_footer.pod
+++ b/docs/busybox_footer.pod
@@ -37,6 +37,7 @@ incorrect, please send in an update.
37=for html <br> 37=for html <br>
38 38
39Emanuele Aina <emanuele.aina@tiscali.it> 39Emanuele Aina <emanuele.aina@tiscali.it>
40
40 run-parts 41 run-parts
41 42
42=for html <br> 43=for html <br>
diff --git a/e2fsprogs/chattr.c b/e2fsprogs/chattr.c
index e1a798727..c1e90d13f 100644
--- a/e2fsprogs/chattr.c
+++ b/e2fsprogs/chattr.c
@@ -21,7 +21,7 @@
21 21
22//usage:#define chattr_trivial_usage 22//usage:#define chattr_trivial_usage
23//usage: IF_NOT_PLATFORM_MINGW32( 23//usage: IF_NOT_PLATFORM_MINGW32(
24//usage: "[-R] [-v VERSION] [-+=AacDdijsStTu] FILE..." 24//usage: "[-R] [-v VERSION] [-p PROJID] [-+=AacDdijsStTu] FILE..."
25//usage: ) 25//usage: )
26//usage: IF_PLATFORM_MINGW32( 26//usage: IF_PLATFORM_MINGW32(
27//usage: "[-R] [-+rhsatn] FILE..." 27//usage: "[-R] [-+rhsatn] FILE..."
@@ -35,24 +35,34 @@
35//usage: ) 35//usage: )
36//usage: "\n -R Recurse" 36//usage: "\n -R Recurse"
37//usage: IF_NOT_PLATFORM_MINGW32( 37//usage: IF_NOT_PLATFORM_MINGW32(
38//usage: "\n -v VER Set version/generation number" 38//usage: "\n -v NUM Set version/generation number"
39//usage: "\n -p NUM Set project number"
39//usage: ) 40//usage: )
40//-V, -f accepted but ignored 41//-V, -f accepted but ignored
41//usage: "\nModifiers:" 42//usage: "\nModifiers:"
42//usage: IF_NOT_PLATFORM_MINGW32( 43//usage: IF_NOT_PLATFORM_MINGW32(
43//usage: "\n -,+,= Remove/add/set attributes" 44//usage: "\n -,+,= Remove/add/set attributes"
44//usage: "\nAttributes:" 45//usage: "\nAttributes:"
45//usage: "\n A Don't track atime" 46//usage: "\n A No atime"
46//usage: "\n a Append mode only" 47//usage: "\n a Append only"
47//usage: "\n c Enable compress" 48//usage: "\n C No copy-on-write"
48//usage: "\n D Write dir contents synchronously" 49//usage: "\n c Compressed"
50//usage: "\n D Synchronous dir updates"
49//usage: "\n d Don't backup with dump" 51//usage: "\n d Don't backup with dump"
50//usage: "\n i Cannot be modified (immutable)" 52//usage: "\n E Encrypted"
51//usage: "\n j Write all data to journal first" 53//usage: "\n e File uses extents"
52//usage: "\n s Zero disk storage when deleted" 54//usage: "\n F Case-insensitive dir"
53//usage: "\n S Write synchronously" 55//usage: "\n I Indexed dir"
54//usage: "\n t Disable tail-merging of partial blocks with other files" 56//usage: "\n i Immutable"
55//usage: "\n u Allow file to be undeleted" 57//usage: "\n j Write data to journal first"
58//usage: "\n N File is stored in inode"
59//usage: "\n P Hierarchical project ID dir"
60//usage: "\n S Synchronous file updates"
61//usage: "\n s Zero storage when deleted"
62//usage: "\n T Top of dir hierarchy"
63//usage: "\n t Don't tail-merge with other files"
64//usage: "\n u Allow undelete"
65//usage: "\n V Verity"
56//usage: ) 66//usage: )
57//usage: IF_PLATFORM_MINGW32( 67//usage: IF_PLATFORM_MINGW32(
58//usage: "\n -,+ Remove/add attributes" 68//usage: "\n -,+ Remove/add attributes"
@@ -68,18 +78,22 @@
68#include "libbb.h" 78#include "libbb.h"
69#include "e2fs_lib.h" 79#include "e2fs_lib.h"
70 80
71#define OPT_ADD 1 81#define OPT_ADD (1 << 0)
72#define OPT_REM 2 82#define OPT_REM (1 << 1)
73#define OPT_SET 4 83#define OPT_SET (1 << 2)
74#define OPT_SET_VER 8 84#define OPT_SET_VER (1 << 3)
85#define OPT_SET_PROJ (1 << 4)
75 86
76struct globals { 87struct globals {
77#if !ENABLE_PLATFORM_MINGW32 88#if !ENABLE_PLATFORM_MINGW32
78 unsigned long version; 89 unsigned version;
79#endif 90#endif
80 unsigned long af; 91 unsigned af;
81 unsigned long rf; 92 unsigned rf;
82 int flags; 93 int flags;
94#if !ENABLE_PLATFORM_MINGW32
95 uint32_t projid;
96#endif
83 smallint recursive; 97 smallint recursive;
84}; 98};
85 99
@@ -93,13 +107,15 @@ static unsigned long get_flag(char c)
93 107
94static char** decode_arg(char **argv, struct globals *gp) 108static char** decode_arg(char **argv, struct globals *gp)
95{ 109{
96 unsigned long *fl; 110 unsigned *fl;
97 const char *arg = *argv; 111 const char *arg = *argv;
98 char opt = *arg; 112 char opt = *arg;
99 113
100 fl = &gp->af; 114 fl = &gp->af;
101 if (opt == '-') { 115 if (opt == '-') {
102 gp->flags |= OPT_REM; 116 /* gp->flags |= OPT_REM; - WRONG, it can be an option */
117 /* testcase: chattr =ae -R FILE should not complain "= is incompatible with - and +" */
118 /* (and should not read flags, with =FLAGS they can be just set directly) */
103 fl = &gp->rf; 119 fl = &gp->rf;
104#if ENABLE_PLATFORM_MINGW32 120#if ENABLE_PLATFORM_MINGW32
105 } else { /* if (opt == '+') */ 121 } else { /* if (opt == '+') */
@@ -136,15 +152,22 @@ static char** decode_arg(char **argv, struct globals *gp)
136 if (*arg == 'v') { 152 if (*arg == 'v') {
137 if (!*++argv) 153 if (!*++argv)
138 bb_show_usage(); 154 bb_show_usage();
139 gp->version = xatoul(*argv); 155 gp->version = xatou(*argv);
140 gp->flags |= OPT_SET_VER; 156 gp->flags |= OPT_SET_VER;
141 continue; 157 continue;
142 } 158 }
143//TODO: "-p PROJECT_NUM" ? 159 if (*arg == 'p') {
160 if (!*++argv)
161 bb_show_usage();
162 gp->projid = xatou32(*argv);
163 gp->flags |= OPT_SET_PROJ;
164 continue;
165 }
144#endif 166#endif
145 /* not a known option, try as an attribute */ 167 /* not a known option, try as an attribute */
168 gp->flags |= OPT_REM;
146 } 169 }
147 *fl |= get_flag(*arg); 170 *fl |= get_flag(*arg); /* aborts on bad flag letter */
148 } 171 }
149 172
150 return argv; 173 return argv;
@@ -154,6 +177,8 @@ static void change_attributes(const char *name, struct globals *gp);
154 177
155static int FAST_FUNC chattr_dir_proc(const char *dir_name, struct dirent *de, void *gp) 178static int FAST_FUNC chattr_dir_proc(const char *dir_name, struct dirent *de, void *gp)
156{ 179{
180//TODO: use de->d_type (if it's not DT_UNKNOWN) to skip !(REG || DIR || LNK) entries without lstat?
181
157 char *path = concat_subpath_file(dir_name, de->d_name); 182 char *path = concat_subpath_file(dir_name, de->d_name);
158 /* path is NULL if de->d_name is "." or "..", else... */ 183 /* path is NULL if de->d_name is "." or "..", else... */
159 if (path) { 184 if (path) {
@@ -165,15 +190,16 @@ static int FAST_FUNC chattr_dir_proc(const char *dir_name, struct dirent *de, vo
165 190
166static void change_attributes(const char *name, struct globals *gp) 191static void change_attributes(const char *name, struct globals *gp)
167{ 192{
168 unsigned long fsflags; 193 unsigned fsflags;
194#if !ENABLE_PLATFORM_MINGW32
195 int fd;
196#endif
169 struct stat st; 197 struct stat st;
170 198
171 if (lstat(name, &st) != 0) { 199 if (lstat(name, &st) != 0) {
172 bb_perror_msg("stat %s", name); 200 bb_perror_msg("can't stat '%s'", name);
173 return; 201 return;
174 } 202 }
175 if (S_ISLNK(st.st_mode) && gp->recursive)
176 return;
177 203
178 /* Don't try to open device files, fifos etc. We probably 204 /* Don't try to open device files, fifos etc. We probably
179 * ought to display an error if the file was explicitly given 205 * ought to display an error if the file was explicitly given
@@ -183,11 +209,58 @@ static void change_attributes(const char *name, struct globals *gp)
183 return; 209 return;
184 210
185#if !ENABLE_PLATFORM_MINGW32 211#if !ENABLE_PLATFORM_MINGW32
186 if (gp->flags & OPT_SET_VER) 212 /* There is no way to run needed ioctls on a symlink.
187 if (fsetversion(name, gp->version) != 0) 213 * open(O_PATH | O_NOFOLLOW) _can_ be used to get a fd referring to the symlink,
188 bb_perror_msg("setting version on %s", name); 214 * but ioctls fail on such a fd (tried on 4.12.0 kernel).
189#endif 215 * e2fsprogs-1.46.2 uses open(O_NOFOLLOW), it fails on symlinks.
216 */
217 fd = open_or_warn(name, O_RDONLY | O_NONBLOCK | O_NOCTTY | O_NOFOLLOW);
218 if (fd >= 0) {
219 int r;
220
221 if (gp->flags & OPT_SET_VER) {
222 r = ioctl(fd, EXT2_IOC_SETVERSION, &gp->version);
223 if (r != 0)
224 bb_perror_msg("setting %s on %s", "version", name);
225 }
190 226
227 if (gp->flags & OPT_SET_PROJ) {
228 struct ext2_fsxattr fsxattr;
229 r = ioctl(fd, EXT2_IOC_FSGETXATTR, &fsxattr);
230 /* note: ^^^ may fail in 32-bit userspace on 64-bit kernel (seen on 4.12.0) */
231 if (r != 0) {
232 bb_perror_msg("getting %s on %s", "project ID", name);
233 } else {
234 fsxattr.fsx_projid = gp->projid;
235 r = ioctl(fd, EXT2_IOC_FSSETXATTR, &fsxattr);
236 if (r != 0)
237 bb_perror_msg("setting %s on %s", "project ID", name);
238 }
239 }
240
241 if (gp->flags & OPT_SET) {
242 fsflags = gp->af;
243 } else {
244 r = ioctl(fd, EXT2_IOC_GETFLAGS, &fsflags);
245 if (r != 0) {
246 bb_perror_msg("getting %s on %s", "flags", name);
247 goto skip_setflags;
248 }
249 /*if (gp->flags & OPT_REM) - not needed, rf is zero otherwise */
250 fsflags &= ~gp->rf;
251 /*if (gp->flags & OPT_ADD) - not needed, af is zero otherwise */
252 fsflags |= gp->af;
253// What is this? And why it's not done for SET case?
254 if (!S_ISDIR(st.st_mode))
255 fsflags &= ~EXT2_DIRSYNC_FL;
256 }
257 r = ioctl(fd, EXT2_IOC_SETFLAGS, &fsflags);
258 if (r != 0)
259 bb_perror_msg("setting %s on %s", "flags", name);
260 skip_setflags:
261 close(fd);
262 }
263#else /* ENABLE_PLATFORM_MINGW32 */
191 if (gp->flags & OPT_SET) { 264 if (gp->flags & OPT_SET) {
192 fsflags = gp->af; 265 fsflags = gp->af;
193 } else { 266 } else {
@@ -199,16 +272,13 @@ static void change_attributes(const char *name, struct globals *gp)
199 fsflags &= ~gp->rf; 272 fsflags &= ~gp->rf;
200 /*if (gp->flags & OPT_ADD) - not needed, af is zero otherwise */ 273 /*if (gp->flags & OPT_ADD) - not needed, af is zero otherwise */
201 fsflags |= gp->af; 274 fsflags |= gp->af;
202#if !ENABLE_PLATFORM_MINGW32
203// What is this? And why it's not done for SET case?
204 if (!S_ISDIR(st.st_mode))
205 fsflags &= ~EXT2_DIRSYNC_FL;
206#endif
207 } 275 }
208 if (fsetflags(name, fsflags) != 0) 276 if (fsetflags(name, fsflags) != 0)
209 bb_perror_msg("setting flags on %s", name); 277 bb_perror_msg("setting flags on %s", name);
210 278
211 skip_setflags: 279 skip_setflags:
280#endif
281
212 if (gp->recursive && S_ISDIR(st.st_mode)) 282 if (gp->recursive && S_ISDIR(st.st_mode))
213 iterate_on_dir(name, chattr_dir_proc, gp); 283 iterate_on_dir(name, chattr_dir_proc, gp);
214} 284}
@@ -245,7 +315,7 @@ int chattr_main(int argc UNUSED_PARAM, char **argv)
245#if ENABLE_PLATFORM_MINGW32 315#if ENABLE_PLATFORM_MINGW32
246 bb_simple_error_msg_and_die("must use - or +"); 316 bb_simple_error_msg_and_die("must use - or +");
247#else 317#else
248 bb_simple_error_msg_and_die("must use '-v', =, - or +"); 318 bb_simple_error_msg_and_die("must use -v, -p, =, - or +");
249#endif 319#endif
250 320
251 /* now run chattr on all the files passed to us */ 321 /* now run chattr on all the files passed to us */
diff --git a/e2fsprogs/e2fs_lib.c b/e2fsprogs/e2fs_lib.c
index d0cacf14c..48c3b68b5 100644
--- a/e2fsprogs/e2fs_lib.c
+++ b/e2fsprogs/e2fs_lib.c
@@ -8,125 +8,7 @@
8#include "libbb.h" 8#include "libbb.h"
9#include "e2fs_lib.h" 9#include "e2fs_lib.h"
10 10
11#if !ENABLE_PLATFORM_MINGW32 11#if ENABLE_PLATFORM_MINGW32
12#define HAVE_EXT2_IOCTLS 1
13
14#if INT_MAX == LONG_MAX
15#define IF_LONG_IS_SAME(...) __VA_ARGS__
16#define IF_LONG_IS_WIDER(...)
17#else
18#define IF_LONG_IS_SAME(...)
19#define IF_LONG_IS_WIDER(...) __VA_ARGS__
20#endif
21
22static void close_silently(int fd)
23{
24 int e = errno;
25 close(fd);
26 errno = e;
27}
28#endif
29
30
31/* Iterate a function on each entry of a directory */
32int iterate_on_dir(const char *dir_name,
33 int FAST_FUNC (*func)(const char *, struct dirent *, void *),
34 void *private)
35{
36 DIR *dir;
37 struct dirent *de;
38
39 dir = opendir(dir_name);
40 if (dir == NULL) {
41 return -1;
42 }
43 while ((de = readdir(dir)) != NULL) {
44 func(dir_name, de, private);
45 }
46 closedir(dir);
47 return 0;
48}
49
50
51#if !ENABLE_PLATFORM_MINGW32
52/* Get/set a file version on an ext2 file system */
53int fgetsetversion(const char *name, unsigned long *get_version, unsigned long set_version)
54{
55#if HAVE_EXT2_IOCTLS
56 int fd, r;
57 IF_LONG_IS_WIDER(int ver;)
58
59 fd = open(name, O_RDONLY | O_NONBLOCK);
60 if (fd == -1)
61 return -1;
62 if (!get_version) {
63 IF_LONG_IS_WIDER(
64 ver = (int) set_version;
65 r = ioctl(fd, EXT2_IOC_SETVERSION, &ver);
66 )
67 IF_LONG_IS_SAME(
68 r = ioctl(fd, EXT2_IOC_SETVERSION, (void*)&set_version);
69 )
70 } else {
71 IF_LONG_IS_WIDER(
72 r = ioctl(fd, EXT2_IOC_GETVERSION, &ver);
73 *get_version = ver;
74 )
75 IF_LONG_IS_SAME(
76 r = ioctl(fd, EXT2_IOC_GETVERSION, (void*)get_version);
77 )
78 }
79 close_silently(fd);
80 return r;
81#else /* ! HAVE_EXT2_IOCTLS */
82 errno = EOPNOTSUPP;
83 return -1;
84#endif /* ! HAVE_EXT2_IOCTLS */
85}
86
87/* Get/set a file flags on an ext2 file system */
88int fgetsetflags(const char *name, unsigned long *get_flags, unsigned long set_flags)
89{
90#if HAVE_EXT2_IOCTLS
91 struct stat buf;
92 int fd, r;
93 IF_LONG_IS_WIDER(int f;)
94
95 if (stat(name, &buf) == 0 /* stat is ok */
96 && !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)
97 ) {
98 goto notsupp;
99 }
100 fd = open(name, O_RDONLY | O_NONBLOCK); /* neither read nor write asked for */
101 if (fd == -1)
102 return -1;
103
104 if (!get_flags) {
105 IF_LONG_IS_WIDER(
106 f = (int) set_flags;
107 r = ioctl(fd, EXT2_IOC_SETFLAGS, &f);
108 )
109 IF_LONG_IS_SAME(
110 r = ioctl(fd, EXT2_IOC_SETFLAGS, (void*)&set_flags);
111 )
112 } else {
113 IF_LONG_IS_WIDER(
114 r = ioctl(fd, EXT2_IOC_GETFLAGS, &f);
115 *get_flags = f;
116 )
117 IF_LONG_IS_SAME(
118 r = ioctl(fd, EXT2_IOC_GETFLAGS, (void*)get_flags);
119 )
120 }
121
122 close_silently(fd);
123 return r;
124 notsupp:
125#endif /* HAVE_EXT2_IOCTLS */
126 errno = EOPNOTSUPP;
127 return -1;
128}
129#else /* ENABLE_PLATFORM_MINGW32 */
130/* Only certain attributes can be set using SetFileAttributes() */ 12/* Only certain attributes can be set using SetFileAttributes() */
131#define CHATTR_MASK (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \ 13#define CHATTR_MASK (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
132 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE | \ 14 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE | \
@@ -134,7 +16,7 @@ int fgetsetflags(const char *name, unsigned long *get_flags, unsigned long set_f
134 FILE_ATTRIBUTE_OFFLINE) 16 FILE_ATTRIBUTE_OFFLINE)
135 17
136/* Get/set file attributes on a Windows file system */ 18/* Get/set file attributes on a Windows file system */
137int fgetsetflags(const char *name, unsigned long *get_flags, unsigned long set_flags) 19int fgetsetflags(const char *name, unsigned *get_flags, unsigned set_flags)
138{ 20{
139 struct stat buf; 21 struct stat buf;
140 22
@@ -156,7 +38,6 @@ int fgetsetflags(const char *name, unsigned long *get_flags, unsigned long set_f
156} 38}
157#endif 39#endif
158 40
159
160#if !ENABLE_PLATFORM_MINGW32 41#if !ENABLE_PLATFORM_MINGW32
161/* Print file attributes on an ext2 file system */ 42/* Print file attributes on an ext2 file system */
162const uint32_t e2attr_flags_value[] ALIGN4 = { 43const uint32_t e2attr_flags_value[] ALIGN4 = {
@@ -164,9 +45,7 @@ const uint32_t e2attr_flags_value[] ALIGN4 = {
164 EXT2_COMPRBLK_FL, 45 EXT2_COMPRBLK_FL,
165 EXT2_DIRTY_FL, 46 EXT2_DIRTY_FL,
166 EXT2_NOCOMPR_FL, 47 EXT2_NOCOMPR_FL,
167 EXT2_ECOMPR_FL,
168#endif 48#endif
169 EXT2_INDEX_FL,
170 EXT2_SECRM_FL, 49 EXT2_SECRM_FL,
171 EXT2_UNRM_FL, 50 EXT2_UNRM_FL,
172 EXT2_SYNC_FL, 51 EXT2_SYNC_FL,
@@ -176,26 +55,31 @@ const uint32_t e2attr_flags_value[] ALIGN4 = {
176 EXT2_NODUMP_FL, 55 EXT2_NODUMP_FL,
177 EXT2_NOATIME_FL, 56 EXT2_NOATIME_FL,
178 EXT2_COMPR_FL, 57 EXT2_COMPR_FL,
58 EXT2_ECOMPR_FL,
179 EXT3_JOURNAL_DATA_FL, 59 EXT3_JOURNAL_DATA_FL,
60 EXT2_INDEX_FL,
180 EXT2_NOTAIL_FL, 61 EXT2_NOTAIL_FL,
181 EXT2_TOPDIR_FL 62 EXT2_TOPDIR_FL,
63 EXT2_EXTENT_FL,
64 EXT2_NOCOW_FL,
65 EXT2_CASEFOLD_FL,
66 EXT2_INLINE_DATA_FL,
67 EXT2_PROJINHERIT_FL,
68 EXT2_VERITY_FL,
182}; 69};
183 70
184const char e2attr_flags_sname[] ALIGN1 = 71const char e2attr_flags_sname[] ALIGN1 =
185#ifdef ENABLE_COMPRESSION 72#ifdef ENABLE_COMPRESSION
186 "BZXE" 73 "BZX"
187#endif 74#endif
188 "I" 75 "suSDiadAcEjItTeCFNPV";
189 "suSDiadAcjtT";
190 76
191static const char e2attr_flags_lname[] ALIGN1 = 77static const char e2attr_flags_lname[] ALIGN1 =
192#ifdef ENABLE_COMPRESSION 78#ifdef ENABLE_COMPRESSION
193 "Compressed_File" "\0" 79 "Compressed_File" "\0"
194 "Compressed_Dirty_File" "\0" 80 "Compressed_Dirty_File" "\0"
195 "Compression_Raw_Access" "\0" 81 "Compression_Raw_Access" "\0"
196 "Compression_Error" "\0"
197#endif 82#endif
198 "Indexed_directory" "\0"
199 "Secure_Deletion" "\0" 83 "Secure_Deletion" "\0"
200 "Undelete" "\0" 84 "Undelete" "\0"
201 "Synchronous_Updates" "\0" 85 "Synchronous_Updates" "\0"
@@ -205,9 +89,17 @@ static const char e2attr_flags_lname[] ALIGN1 =
205 "No_Dump" "\0" 89 "No_Dump" "\0"
206 "No_Atime" "\0" 90 "No_Atime" "\0"
207 "Compression_Requested" "\0" 91 "Compression_Requested" "\0"
92 "Encrypted" "\0"
208 "Journaled_Data" "\0" 93 "Journaled_Data" "\0"
94 "Indexed_directory" "\0"
209 "No_Tailmerging" "\0" 95 "No_Tailmerging" "\0"
210 "Top_of_Directory_Hierarchies" "\0" 96 "Top_of_Directory_Hierarchies" "\0"
97 "Extents" "\0"
98 "No_COW" "\0"
99 "Casefold" "\0"
100 "Inline_Data" "\0"
101 "Project_Hierarchy" "\0"
102 "Verity" "\0"
211 /* Another trailing NUL is added by compiler */; 103 /* Another trailing NUL is added by compiler */;
212#else /* ENABLE_PLATFORM_MINGW32 */ 104#else /* ENABLE_PLATFORM_MINGW32 */
213/* Print file attributes on a Windows file system */ 105/* Print file attributes on a Windows file system */
@@ -243,36 +135,41 @@ static const char e2attr_flags_lname[] ALIGN1 =
243 /* Another trailing NUL is added by compiler */; 135 /* Another trailing NUL is added by compiler */;
244#endif 136#endif
245 137
246void print_e2flags(FILE *f, unsigned long flags, unsigned options) 138void print_e2flags_long(unsigned flags)
247{ 139{
248 const uint32_t *fv; 140 const uint32_t *fv;
249 const char *fn; 141 const char *fn;
142 int first = 1;
250 143
251 fv = e2attr_flags_value; 144 fv = e2attr_flags_value;
252 if (options & PFOPT_LONG) { 145 fn = e2attr_flags_lname;
253 int first = 1; 146 do {
254 fn = e2attr_flags_lname; 147 if (flags & *fv) {
255 do { 148 if (!first)
256 if (flags & *fv) { 149 fputs(", ", stdout);
257 if (!first) 150 fputs(fn, stdout);
258 fputs(", ", f); 151 first = 0;
259 fputs(fn, f); 152 }
260 first = 0; 153 fv++;
261 } 154 fn += strlen(fn) + 1;
262 fv++; 155 } while (*fn);
263 fn += strlen(fn) + 1; 156 if (first)
264 } while (*fn); 157 fputs("---", stdout);
265 if (first) 158}
266 fputs("---", f); 159
267 } else { 160void print_e2flags(unsigned flags)
268 fn = e2attr_flags_sname; 161{
269 do { 162 const uint32_t *fv;
270 char c = '-'; 163 const char *fn;
271 if (flags & *fv) 164
272 c = *fn; 165 fv = e2attr_flags_value;
273 fputc(c, f); 166 fn = e2attr_flags_sname;
274 fv++; 167 do {
275 fn++; 168 char c = '-';
276 } while (*fn); 169 if (flags & *fv)
277 } 170 c = *fn;
171 putchar(c);
172 fv++;
173 fn++;
174 } while (*fn);
278} 175}
diff --git a/e2fsprogs/e2fs_lib.h b/e2fsprogs/e2fs_lib.h
index 4a4d4cc27..aa92e63af 100644
--- a/e2fsprogs/e2fs_lib.h
+++ b/e2fsprogs/e2fs_lib.h
@@ -11,25 +11,16 @@
11 11
12PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN 12PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
13 13
14/* Iterate a function on each entry of a directory */ 14#if ENABLE_PLATFORM_MINGW32
15int iterate_on_dir(const char *dir_name, 15/* Get/set a file flags */
16 int FAST_FUNC (*func)(const char *, struct dirent *, void *), 16int fgetsetflags(const char *name, unsigned *get_flags, unsigned set_flags);
17 void *private);
18
19/* Get/set a file version on an ext2 file system */
20int fgetsetversion(const char *name, unsigned long *get_version, unsigned long set_version);
21#define fgetversion(name, version) fgetsetversion(name, version, 0)
22#define fsetversion(name, version) fgetsetversion(name, NULL, version)
23
24/* Get/set a file flags on an ext2 file system */
25int fgetsetflags(const char *name, unsigned long *get_flags, unsigned long set_flags);
26#define fgetflags(name, flags) fgetsetflags(name, flags, 0) 17#define fgetflags(name, flags) fgetsetflags(name, flags, 0)
27#define fsetflags(name, flags) fgetsetflags(name, NULL, flags) 18#define fsetflags(name, flags) fgetsetflags(name, NULL, flags)
19#endif
28 20
29/* Must be 1 for compatibility with 'int long_format'. */
30#define PFOPT_LONG 1
31/* Print file attributes on an ext2 file system */ 21/* Print file attributes on an ext2 file system */
32void print_e2flags(FILE *f, unsigned long flags, unsigned options); 22void print_e2flags_long(unsigned flags);
23void print_e2flags(unsigned flags);
33 24
34extern const uint32_t e2attr_flags_value[]; 25extern const uint32_t e2attr_flags_value[];
35extern const char e2attr_flags_sname[]; 26extern const char e2attr_flags_sname[];
diff --git a/e2fsprogs/lsattr.c b/e2fsprogs/lsattr.c
index ed7b67c6f..d82861e14 100644
--- a/e2fsprogs/lsattr.c
+++ b/e2fsprogs/lsattr.c
@@ -22,7 +22,7 @@
22 22
23//usage:#define lsattr_trivial_usage 23//usage:#define lsattr_trivial_usage
24//usage: IF_NOT_PLATFORM_MINGW32( 24//usage: IF_NOT_PLATFORM_MINGW32(
25//usage: "[-Radlv] [FILE]..." 25//usage: "[-Radlpv] [FILE]..."
26//usage: ) 26//usage: )
27//usage: IF_PLATFORM_MINGW32( 27//usage: IF_PLATFORM_MINGW32(
28//usage: "[-Radl] [FILE]..." 28//usage: "[-Radl] [FILE]..."
@@ -30,10 +30,12 @@
30//usage:#define lsattr_full_usage "\n\n" 30//usage:#define lsattr_full_usage "\n\n"
31//usage: "List ext2 file attributes\n" 31//usage: "List ext2 file attributes\n"
32//usage: "\n -R Recurse" 32//usage: "\n -R Recurse"
33//usage: "\n -a Don't hide entries starting with ." 33//usage: "\n -a Include names starting with ."
34//usage: "\n -d List directory entries instead of contents" 34//usage: "\n -d List directory names, not contents"
35// -a,-d text should match ls --help
35//usage: "\n -l List long flag names" 36//usage: "\n -l List long flag names"
36//usage: IF_NOT_PLATFORM_MINGW32( 37//usage: IF_NOT_PLATFORM_MINGW32(
38//usage: "\n -p List project ID"
37//usage: "\n -v List version/generation number" 39//usage: "\n -v List version/generation number"
38//usage: ) 40//usage: )
39 41
@@ -41,43 +43,71 @@
41#include "e2fs_lib.h" 43#include "e2fs_lib.h"
42 44
43enum { 45enum {
44 OPT_RECUR = 0x1, 46 OPT_RECUR = 1 << 0,
45 OPT_ALL = 0x2, 47 OPT_ALL = 1 << 1,
46 OPT_DIRS_OPT = 0x4, 48 OPT_DIRS_OPT = 1 << 2,
47 OPT_PF_LONG = 0x8, 49 OPT_PF_LONG = 1 << 3,
48 OPT_GENERATION = 0x10, 50 OPT_GENERATION = 1 << 4,
51 OPT_PROJID = 1 << 5,
49}; 52};
50 53
51static void list_attributes(const char *name) 54static void list_attributes(const char *name)
52{ 55{
53 unsigned long fsflags; 56 unsigned fsflags;
54#if !ENABLE_PLATFORM_MINGW32 57#if !ENABLE_PLATFORM_MINGW32
55 unsigned long generation; 58 int fd, r;
56#endif 59
57 60 /* There is no way to run needed ioctls on a symlink.
58 if (fgetflags(name, &fsflags) != 0) 61 * open(O_PATH | O_NOFOLLOW) _can_ be used to get a fd referring to the symlink,
59 goto read_err; 62 * but ioctls fail on such a fd (tried on 4.12.0 kernel).
63 * e2fsprogs-1.46.2 uses open(O_NOFOLLOW), it fails on symlinks.
64 */
65 fd = open_or_warn(name, O_RDONLY | O_NONBLOCK | O_NOCTTY | O_NOFOLLOW);
66 if (fd < 0)
67 return;
68
69 if (option_mask32 & OPT_PROJID) {
70 struct ext2_fsxattr fsxattr;
71 r = ioctl(fd, EXT2_IOC_FSGETXATTR, &fsxattr);
72 /* note: ^^^ may fail in 32-bit userspace on 64-bit kernel (seen on 4.12.0) */
73 if (r != 0)
74 goto read_err;
75 printf("%5u ", (unsigned)fsxattr.fsx_projid);
76 }
60 77
61#if !ENABLE_PLATFORM_MINGW32
62 if (option_mask32 & OPT_GENERATION) { 78 if (option_mask32 & OPT_GENERATION) {
63 if (fgetversion(name, &generation) != 0) 79 unsigned generation;
80 r = ioctl(fd, EXT2_IOC_GETVERSION, &generation);
81 if (r != 0)
64 goto read_err; 82 goto read_err;
65 printf("%5lu ", generation); 83 printf("%-10u ", generation);
66 } 84 }
85
86 r = ioctl(fd, EXT2_IOC_GETFLAGS, &fsflags);
87 if (r != 0)
88 goto read_err;
89
90 close(fd);
91#else /* ENABLE_PLATFORM_MINGW32 */
92 if (fgetflags(name, &fsflags) != 0)
93 goto read_err;
67#endif 94#endif
68 95
69 if (option_mask32 & OPT_PF_LONG) { 96 if (option_mask32 & OPT_PF_LONG) {
70 printf("%-28s ", name); 97 printf("%-28s ", name);
71 print_e2flags(stdout, fsflags, PFOPT_LONG); 98 print_e2flags_long(fsflags);
72 bb_putchar('\n'); 99 bb_putchar('\n');
73 } else { 100 } else {
74 print_e2flags(stdout, fsflags, 0); 101 print_e2flags(fsflags);
75 printf(" %s\n", name); 102 printf(" %s\n", name);
76 } 103 }
77 104
78 return; 105 return;
79 read_err: 106 read_err:
80 bb_perror_msg("reading %s", name); 107 bb_perror_msg("reading %s", name);
108#if !ENABLE_PLATFORM_MINGW32
109 close(fd);
110#endif
81} 111}
82 112
83static int FAST_FUNC lsattr_dir_proc(const char *dir_name, 113static int FAST_FUNC lsattr_dir_proc(const char *dir_name,
@@ -90,9 +120,13 @@ static int FAST_FUNC lsattr_dir_proc(const char *dir_name,
90 path = concat_path_file(dir_name, de->d_name); 120 path = concat_path_file(dir_name, de->d_name);
91 121
92 if (lstat(path, &st) != 0) 122 if (lstat(path, &st) != 0)
93 bb_perror_msg("stat %s", path); 123 bb_perror_msg("can't stat '%s'", path);
124
94 else if (de->d_name[0] != '.' || (option_mask32 & OPT_ALL)) { 125 else if (de->d_name[0] != '.' || (option_mask32 & OPT_ALL)) {
95 list_attributes(path); 126 /* Don't try to open device files, fifos etc */
127 if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode) || S_ISDIR(st.st_mode))
128 list_attributes(path);
129
96 if (S_ISDIR(st.st_mode) && (option_mask32 & OPT_RECUR) 130 if (S_ISDIR(st.st_mode) && (option_mask32 & OPT_RECUR)
97 && !DOT_OR_DOTDOT(de->d_name) 131 && !DOT_OR_DOTDOT(de->d_name)
98 ) { 132 ) {
@@ -111,7 +145,7 @@ static void lsattr_args(const char *name)
111 struct stat st; 145 struct stat st;
112 146
113 if (lstat(name, &st) == -1) { 147 if (lstat(name, &st) == -1) {
114 bb_perror_msg("stat %s", name); 148 bb_perror_msg("can't stat '%s'", name);
115 } else if (S_ISDIR(st.st_mode) && !(option_mask32 & OPT_DIRS_OPT)) { 149 } else if (S_ISDIR(st.st_mode) && !(option_mask32 & OPT_DIRS_OPT)) {
116 iterate_on_dir(name, lsattr_dir_proc, NULL); 150 iterate_on_dir(name, lsattr_dir_proc, NULL);
117 } else { 151 } else {
@@ -125,7 +159,7 @@ int lsattr_main(int argc UNUSED_PARAM, char **argv)
125#if ENABLE_PLATFORM_MINGW32 159#if ENABLE_PLATFORM_MINGW32
126 getopt32(argv, "Radl"); 160 getopt32(argv, "Radl");
127#else 161#else
128 getopt32(argv, "Radlv"); 162 getopt32(argv, "Radlvp");
129#endif 163#endif
130 argv += optind; 164 argv += optind;
131 165
diff --git a/editors/awk.c b/editors/awk.c
index 41a57ea0c..9b9b202db 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -66,6 +66,8 @@
66#endif 66#endif
67#ifndef debug_printf_parse 67#ifndef debug_printf_parse
68# define debug_printf_parse(...) (fprintf(stderr, __VA_ARGS__)) 68# define debug_printf_parse(...) (fprintf(stderr, __VA_ARGS__))
69#else
70# define debug_parse_print_tc(...) ((void)0)
69#endif 71#endif
70 72
71 73
@@ -210,13 +212,13 @@ typedef struct tsplitter_s {
210#define TC_SEQTERM (1 << 1) /* ) */ 212#define TC_SEQTERM (1 << 1) /* ) */
211#define TC_REGEXP (1 << 2) /* /.../ */ 213#define TC_REGEXP (1 << 2) /* /.../ */
212#define TC_OUTRDR (1 << 3) /* | > >> */ 214#define TC_OUTRDR (1 << 3) /* | > >> */
213#define TC_UOPPOST (1 << 4) /* unary postfix operator */ 215#define TC_UOPPOST (1 << 4) /* unary postfix operator ++ -- */
214#define TC_UOPPRE1 (1 << 5) /* unary prefix operator */ 216#define TC_UOPPRE1 (1 << 5) /* unary prefix operator ++ -- $ */
215#define TC_BINOPX (1 << 6) /* two-opnd operator */ 217#define TC_BINOPX (1 << 6) /* two-opnd operator */
216#define TC_IN (1 << 7) 218#define TC_IN (1 << 7)
217#define TC_COMMA (1 << 8) 219#define TC_COMMA (1 << 8)
218#define TC_PIPE (1 << 9) /* input redirection pipe */ 220#define TC_PIPE (1 << 9) /* input redirection pipe */
219#define TC_UOPPRE2 (1 << 10) /* unary prefix operator */ 221#define TC_UOPPRE2 (1 << 10) /* unary prefix operator + - ! */
220#define TC_ARRTERM (1 << 11) /* ] */ 222#define TC_ARRTERM (1 << 11) /* ] */
221#define TC_GRPSTART (1 << 12) /* { */ 223#define TC_GRPSTART (1 << 12) /* { */
222#define TC_GRPTERM (1 << 13) /* } */ 224#define TC_GRPTERM (1 << 13) /* } */
@@ -243,14 +245,51 @@ typedef struct tsplitter_s {
243#define TC_STRING (1 << 29) 245#define TC_STRING (1 << 29)
244#define TC_NUMBER (1 << 30) 246#define TC_NUMBER (1 << 30)
245 247
246#define TC_UOPPRE (TC_UOPPRE1 | TC_UOPPRE2) 248#ifndef debug_parse_print_tc
249#define debug_parse_print_tc(n) do { \
250if ((n) & TC_SEQSTART) debug_printf_parse(" SEQSTART"); \
251if ((n) & TC_SEQTERM ) debug_printf_parse(" SEQTERM" ); \
252if ((n) & TC_REGEXP ) debug_printf_parse(" REGEXP" ); \
253if ((n) & TC_OUTRDR ) debug_printf_parse(" OUTRDR" ); \
254if ((n) & TC_UOPPOST ) debug_printf_parse(" UOPPOST" ); \
255if ((n) & TC_UOPPRE1 ) debug_printf_parse(" UOPPRE1" ); \
256if ((n) & TC_BINOPX ) debug_printf_parse(" BINOPX" ); \
257if ((n) & TC_IN ) debug_printf_parse(" IN" ); \
258if ((n) & TC_COMMA ) debug_printf_parse(" COMMA" ); \
259if ((n) & TC_PIPE ) debug_printf_parse(" PIPE" ); \
260if ((n) & TC_UOPPRE2 ) debug_printf_parse(" UOPPRE2" ); \
261if ((n) & TC_ARRTERM ) debug_printf_parse(" ARRTERM" ); \
262if ((n) & TC_GRPSTART) debug_printf_parse(" GRPSTART"); \
263if ((n) & TC_GRPTERM ) debug_printf_parse(" GRPTERM" ); \
264if ((n) & TC_SEMICOL ) debug_printf_parse(" SEMICOL" ); \
265if ((n) & TC_NEWLINE ) debug_printf_parse(" NEWLINE" ); \
266if ((n) & TC_STATX ) debug_printf_parse(" STATX" ); \
267if ((n) & TC_WHILE ) debug_printf_parse(" WHILE" ); \
268if ((n) & TC_ELSE ) debug_printf_parse(" ELSE" ); \
269if ((n) & TC_BUILTIN ) debug_printf_parse(" BUILTIN" ); \
270if ((n) & TC_LENGTH ) debug_printf_parse(" LENGTH" ); \
271if ((n) & TC_GETLINE ) debug_printf_parse(" GETLINE" ); \
272if ((n) & TC_FUNCDECL) debug_printf_parse(" FUNCDECL"); \
273if ((n) & TC_BEGIN ) debug_printf_parse(" BEGIN" ); \
274if ((n) & TC_END ) debug_printf_parse(" END" ); \
275if ((n) & TC_EOF ) debug_printf_parse(" EOF" ); \
276if ((n) & TC_VARIABLE) debug_printf_parse(" VARIABLE"); \
277if ((n) & TC_ARRAY ) debug_printf_parse(" ARRAY" ); \
278if ((n) & TC_FUNCTION) debug_printf_parse(" FUNCTION"); \
279if ((n) & TC_STRING ) debug_printf_parse(" STRING" ); \
280if ((n) & TC_NUMBER ) debug_printf_parse(" NUMBER" ); \
281} while (0)
282#endif
247 283
248/* combined token classes */ 284/* combined token classes */
285#define TC_UOPPRE (TC_UOPPRE1 | TC_UOPPRE2)
286
249#define TC_BINOP (TC_BINOPX | TC_COMMA | TC_PIPE | TC_IN) 287#define TC_BINOP (TC_BINOPX | TC_COMMA | TC_PIPE | TC_IN)
250//#define TC_UNARYOP (TC_UOPPRE | TC_UOPPOST) 288//#define TC_UNARYOP (TC_UOPPRE | TC_UOPPOST)
251#define TC_OPERAND (TC_VARIABLE | TC_ARRAY | TC_FUNCTION \ 289#define TC_OPERAND (TC_VARIABLE | TC_ARRAY | TC_FUNCTION \
252 | TC_BUILTIN | TC_LENGTH | TC_GETLINE \ 290 | TC_BUILTIN | TC_LENGTH | TC_GETLINE \
253 | TC_SEQSTART | TC_STRING | TC_NUMBER) 291 | TC_SEQSTART | TC_STRING | TC_NUMBER)
292#define TC_LVALUE (TC_VARIABLE | TC_ARRAY)
254 293
255#define TC_STATEMNT (TC_STATX | TC_WHILE) 294#define TC_STATEMNT (TC_STATX | TC_WHILE)
256#define TC_OPTERM (TC_SEMICOL | TC_NEWLINE) 295#define TC_OPTERM (TC_SEMICOL | TC_NEWLINE)
@@ -284,7 +323,6 @@ typedef struct tsplitter_s {
284#define OF_CHECKED 0x200000 323#define OF_CHECKED 0x200000
285#define OF_REQUIRED 0x400000 324#define OF_REQUIRED 0x400000
286 325
287
288/* combined operator flags */ 326/* combined operator flags */
289#define xx 0 327#define xx 0
290#define xV OF_RES2 328#define xV OF_RES2
@@ -313,10 +351,8 @@ typedef struct tsplitter_s {
313#define PRIMASK2 0x7E000000 351#define PRIMASK2 0x7E000000
314 352
315/* Operation classes */ 353/* Operation classes */
316
317#define SHIFT_TIL_THIS 0x0600 354#define SHIFT_TIL_THIS 0x0600
318#define RECUR_FROM_THIS 0x1000 355#define RECUR_FROM_THIS 0x1000
319
320enum { 356enum {
321 OC_DELETE = 0x0100, OC_EXEC = 0x0200, OC_NEWSOURCE = 0x0300, 357 OC_DELETE = 0x0100, OC_EXEC = 0x0200, OC_NEWSOURCE = 0x0300,
322 OC_PRINT = 0x0400, OC_PRINTF = 0x0500, OC_WALKINIT = 0x0600, 358 OC_PRINT = 0x0400, OC_PRINTF = 0x0500, OC_WALKINIT = 0x0600,
@@ -411,13 +447,16 @@ static const uint32_t tokeninfo[] ALIGN4 = {
411 OC_REGEXP, 447 OC_REGEXP,
412 xS|'a', xS|'w', xS|'|', 448 xS|'a', xS|'w', xS|'|',
413 OC_UNARY|xV|P(9)|'p', OC_UNARY|xV|P(9)|'m', 449 OC_UNARY|xV|P(9)|'p', OC_UNARY|xV|P(9)|'m',
414 OC_UNARY|xV|P(9)|'P', OC_UNARY|xV|P(9)|'M', OC_FIELD|xV|P(5), 450#define TI_PREINC (OC_UNARY|xV|P(9)|'P')
451#define TI_PREDEC (OC_UNARY|xV|P(9)|'M')
452 TI_PREINC, TI_PREDEC, OC_FIELD|xV|P(5),
415 OC_COMPARE|VV|P(39)|5, OC_MOVE|VV|P(74), OC_REPLACE|NV|P(74)|'+', OC_REPLACE|NV|P(74)|'-', 453 OC_COMPARE|VV|P(39)|5, OC_MOVE|VV|P(74), OC_REPLACE|NV|P(74)|'+', OC_REPLACE|NV|P(74)|'-',
416 OC_REPLACE|NV|P(74)|'*', OC_REPLACE|NV|P(74)|'/', OC_REPLACE|NV|P(74)|'%', OC_REPLACE|NV|P(74)|'&', 454 OC_REPLACE|NV|P(74)|'*', OC_REPLACE|NV|P(74)|'/', OC_REPLACE|NV|P(74)|'%', OC_REPLACE|NV|P(74)|'&',
417 OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-', OC_REPLACE|NV|P(74)|'&', OC_BINARY|NV|P(15)|'&', 455 OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-', OC_REPLACE|NV|P(74)|'&', OC_BINARY|NV|P(15)|'&',
418 OC_BINARY|NV|P(25)|'/', OC_BINARY|NV|P(25)|'%', OC_BINARY|NV|P(15)|'&', OC_BINARY|NV|P(25)|'*', 456 OC_BINARY|NV|P(25)|'/', OC_BINARY|NV|P(25)|'%', OC_BINARY|NV|P(15)|'&', OC_BINARY|NV|P(25)|'*',
419 OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3, OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1, 457 OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3, OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1,
420 OC_COMPARE|VV|P(39)|2, OC_MATCH|Sx|P(45)|'!', OC_MATCH|Sx|P(45)|'~', OC_LAND|Vx|P(55), 458#define TI_LESS (OC_COMPARE|VV|P(39)|2)
459 TI_LESS, OC_MATCH|Sx|P(45)|'!', OC_MATCH|Sx|P(45)|'~', OC_LAND|Vx|P(55),
421 OC_LOR|Vx|P(59), OC_TERNARY|Vx|P(64)|'?', OC_COLON|xx|P(67)|':', 460 OC_LOR|Vx|P(59), OC_TERNARY|Vx|P(64)|'?', OC_COLON|xx|P(67)|':',
422 OC_IN|SV|P(49), /* TC_IN */ 461 OC_IN|SV|P(49), /* TC_IN */
423 OC_COMMA|SS|P(80), 462 OC_COMMA|SS|P(80),
@@ -1074,6 +1113,10 @@ static uint32_t next_token(uint32_t expected)
1074 uint32_t tc; 1113 uint32_t tc;
1075 const uint32_t *ti; 1114 const uint32_t *ti;
1076 1115
1116 debug_printf_parse("%s() expected(%x):", __func__, expected);
1117 debug_parse_print_tc(expected);
1118 debug_printf_parse("\n");
1119
1077 if (t_rollback) { 1120 if (t_rollback) {
1078 debug_printf_parse("%s: using rolled-back token\n", __func__); 1121 debug_printf_parse("%s: using rolled-back token\n", __func__);
1079 t_rollback = FALSE; 1122 t_rollback = FALSE;
@@ -1178,6 +1221,8 @@ static uint32_t next_token(uint32_t expected)
1178 if (!isalnum_(*p)) 1221 if (!isalnum_(*p))
1179 syntax_error(EMSG_UNEXP_TOKEN); /* no */ 1222 syntax_error(EMSG_UNEXP_TOKEN); /* no */
1180 /* yes */ 1223 /* yes */
1224/* "move name one char back" trick: we need a byte for NUL terminator */
1225/* NB: this results in argv[i][-1] being used (!!!) in e.g. "awk -e 'NAME'" case */
1181 t_string = --p; 1226 t_string = --p;
1182 while (isalnum_(*++p)) { 1227 while (isalnum_(*++p)) {
1183 p[-1] = *p; 1228 p[-1] = *p;
@@ -1230,7 +1275,9 @@ static uint32_t next_token(uint32_t expected)
1230 EMSG_UNEXP_EOS : EMSG_UNEXP_TOKEN); 1275 EMSG_UNEXP_EOS : EMSG_UNEXP_TOKEN);
1231 } 1276 }
1232 1277
1233 debug_printf_parse("%s: returning, ltclass:%x t_double:%f\n", __func__, ltclass, t_double); 1278 debug_printf_parse("%s: returning, t_double:%f ltclass:", __func__, t_double);
1279 debug_parse_print_tc(ltclass);
1280 debug_printf_parse("\n");
1234 return ltclass; 1281 return ltclass;
1235#undef concat_inserted 1282#undef concat_inserted
1236#undef save_tclass 1283#undef save_tclass
@@ -1270,7 +1317,7 @@ static node *condition(void)
1270 1317
1271/* parse expression terminated by given argument, return ptr 1318/* parse expression terminated by given argument, return ptr
1272 * to built subtree. Terminator is eaten by parse_expr */ 1319 * to built subtree. Terminator is eaten by parse_expr */
1273static node *parse_expr(uint32_t iexp) 1320static node *parse_expr(uint32_t term_tc)
1274{ 1321{
1275 node sn; 1322 node sn;
1276 node *cn = &sn; 1323 node *cn = &sn;
@@ -1278,15 +1325,17 @@ static node *parse_expr(uint32_t iexp)
1278 uint32_t tc, xtc; 1325 uint32_t tc, xtc;
1279 var *v; 1326 var *v;
1280 1327
1281 debug_printf_parse("%s(%x)\n", __func__, iexp); 1328 debug_printf_parse("%s() term_tc(%x):", __func__, term_tc);
1329 debug_parse_print_tc(term_tc);
1330 debug_printf_parse("\n");
1282 1331
1283 sn.info = PRIMASK; 1332 sn.info = PRIMASK;
1284 sn.r.n = sn.a.n = glptr = NULL; 1333 sn.r.n = sn.a.n = glptr = NULL;
1285 xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP | iexp; 1334 xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP | term_tc;
1286 1335
1287 while (!((tc = next_token(xtc)) & iexp)) { 1336 while (!((tc = next_token(xtc)) & term_tc)) {
1288 1337
1289 if (glptr && (t_info == (OC_COMPARE | VV | P(39) | 2))) { 1338 if (glptr && (t_info == TI_LESS)) {
1290 /* input redirection (<) attached to glptr node */ 1339 /* input redirection (<) attached to glptr node */
1291 debug_printf_parse("%s: input redir\n", __func__); 1340 debug_printf_parse("%s: input redir\n", __func__);
1292 cn = glptr->l.n = new_node(OC_CONCAT | SS | P(37)); 1341 cn = glptr->l.n = new_node(OC_CONCAT | SS | P(37));
@@ -1317,25 +1366,28 @@ static node *parse_expr(uint32_t iexp)
1317 next_token(TC_GETLINE); 1366 next_token(TC_GETLINE);
1318 /* give maximum priority to this pipe */ 1367 /* give maximum priority to this pipe */
1319 cn->info &= ~PRIMASK; 1368 cn->info &= ~PRIMASK;
1320 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp; 1369 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | term_tc;
1321 } 1370 }
1322 } else { 1371 } else {
1323 cn->r.n = vn; 1372 cn->r.n = vn;
1324 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp; 1373 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | term_tc;
1325 } 1374 }
1326 vn->a.n = cn; 1375 vn->a.n = cn;
1327 1376
1328 } else { 1377 } else {
1329 debug_printf_parse("%s: other\n", __func__); 1378 debug_printf_parse("%s: other, t_info:%x\n", __func__, t_info);
1330 /* for operands and prefix-unary operators, attach them 1379 /* for operands and prefix-unary operators, attach them
1331 * to last node */ 1380 * to last node */
1332 vn = cn; 1381 vn = cn;
1333 cn = vn->r.n = new_node(t_info); 1382 cn = vn->r.n = new_node(t_info);
1334 cn->a.n = vn; 1383 cn->a.n = vn;
1384
1335 xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP; 1385 xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP;
1386 if (t_info == TI_PREINC || t_info == TI_PREDEC)
1387 xtc = TC_LVALUE | TC_UOPPRE1;
1336 if (tc & (TC_OPERAND | TC_REGEXP)) { 1388 if (tc & (TC_OPERAND | TC_REGEXP)) {
1337 debug_printf_parse("%s: TC_OPERAND | TC_REGEXP\n", __func__); 1389 debug_printf_parse("%s: TC_OPERAND | TC_REGEXP\n", __func__);
1338 xtc = TC_UOPPRE | TC_UOPPOST | TC_BINOP | TC_OPERAND | iexp; 1390 xtc = TC_UOPPRE | TC_UOPPOST | TC_BINOP | TC_OPERAND | term_tc;
1339 /* one should be very careful with switch on tclass - 1391 /* one should be very careful with switch on tclass -
1340 * only simple tclasses should be used! */ 1392 * only simple tclasses should be used! */
1341 switch (tc) { 1393 switch (tc) {
@@ -1392,7 +1444,7 @@ static node *parse_expr(uint32_t iexp)
1392 case TC_GETLINE: 1444 case TC_GETLINE:
1393 debug_printf_parse("%s: TC_GETLINE\n", __func__); 1445 debug_printf_parse("%s: TC_GETLINE\n", __func__);
1394 glptr = cn; 1446 glptr = cn;
1395 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp; 1447 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | term_tc;
1396 break; 1448 break;
1397 1449
1398 case TC_BUILTIN: 1450 case TC_BUILTIN:
@@ -1607,6 +1659,8 @@ static void parse_program(char *p)
1607 func *f; 1659 func *f;
1608 var *v; 1660 var *v;
1609 1661
1662 debug_printf_parse("%s()\n", __func__);
1663
1610 g_pos = p; 1664 g_pos = p;
1611 t_lineno = 1; 1665 t_lineno = 1;
1612 while ((tclass = next_token(TC_EOF | TC_OPSEQ | TC_GRPSTART | 1666 while ((tclass = next_token(TC_EOF | TC_OPSEQ | TC_GRPSTART |
@@ -1749,12 +1803,22 @@ static char* qrealloc(char *b, int n, int *size)
1749/* resize field storage space */ 1803/* resize field storage space */
1750static void fsrealloc(int size) 1804static void fsrealloc(int size)
1751{ 1805{
1752 int i; 1806 int i, newsize;
1753 1807
1754 if (size >= maxfields) { 1808 if (size >= maxfields) {
1809 /* Sanity cap, easier than catering for overflows */
1810 if (size > 0xffffff)
1811 bb_die_memory_exhausted();
1812
1755 i = maxfields; 1813 i = maxfields;
1756 maxfields = size + 16; 1814 maxfields = size + 16;
1757 Fields = xrealloc(Fields, maxfields * sizeof(Fields[0])); 1815
1816 newsize = maxfields * sizeof(Fields[0]);
1817 debug_printf_eval("fsrealloc: xrealloc(%p, %u)\n", Fields, newsize);
1818 Fields = xrealloc(Fields, newsize);
1819 debug_printf_eval("fsrealloc: Fields=%p..%p\n", Fields, (char*)Fields + newsize - 1);
1820 /* ^^^ did Fields[] move? debug aid for L.v getting "upstaged" by R.v in evaluate() */
1821
1758 for (; i < maxfields; i++) { 1822 for (; i < maxfields; i++) {
1759 Fields[i].type = VF_SPECIAL; 1823 Fields[i].type = VF_SPECIAL;
1760 Fields[i].string = NULL; 1824 Fields[i].string = NULL;
@@ -2633,20 +2697,30 @@ static var *evaluate(node *op, var *res)
2633 /* execute inevitable things */ 2697 /* execute inevitable things */
2634 if (opinfo & OF_RES1) 2698 if (opinfo & OF_RES1)
2635 L.v = evaluate(op1, v1); 2699 L.v = evaluate(op1, v1);
2636 if (opinfo & OF_RES2)
2637 R.v = evaluate(op->r.n, v1+1);
2638 if (opinfo & OF_STR1) { 2700 if (opinfo & OF_STR1) {
2639 L.s = getvar_s(L.v); 2701 L.s = getvar_s(L.v);
2640 debug_printf_eval("L.s:'%s'\n", L.s); 2702 debug_printf_eval("L.s:'%s'\n", L.s);
2641 } 2703 }
2642 if (opinfo & OF_STR2) {
2643 R.s = getvar_s(R.v);
2644 debug_printf_eval("R.s:'%s'\n", R.s);
2645 }
2646 if (opinfo & OF_NUM1) { 2704 if (opinfo & OF_NUM1) {
2647 L_d = getvar_i(L.v); 2705 L_d = getvar_i(L.v);
2648 debug_printf_eval("L_d:%f\n", L_d); 2706 debug_printf_eval("L_d:%f\n", L_d);
2649 } 2707 }
2708 /* NB: Must get string/numeric values of L (done above)
2709 * _before_ evaluate()'ing R.v: if both L and R are $NNNs,
2710 * and right one is large, then L.v points to Fields[NNN1],
2711 * second evaluate() reallocates and moves (!) Fields[],
2712 * R.v points to Fields[NNN2] but L.v now points to freed mem!
2713 * (Seen trying to evaluate "$444 $44444")
2714 */
2715 if (opinfo & OF_RES2) {
2716 R.v = evaluate(op->r.n, v1+1);
2717 //TODO: L.v may be invalid now, set L.v to NULL to catch bugs?
2718 //L.v = NULL;
2719 }
2720 if (opinfo & OF_STR2) {
2721 R.s = getvar_s(R.v);
2722 debug_printf_eval("R.s:'%s'\n", R.s);
2723 }
2650 2724
2651 debug_printf_eval("switch(0x%x)\n", XC(opinfo & OPCLSMASK)); 2725 debug_printf_eval("switch(0x%x)\n", XC(opinfo & OPCLSMASK));
2652 switch (XC(opinfo & OPCLSMASK)) { 2726 switch (XC(opinfo & OPCLSMASK)) {
@@ -2655,6 +2729,7 @@ static var *evaluate(node *op, var *res)
2655 2729
2656 /* test pattern */ 2730 /* test pattern */
2657 case XC( OC_TEST ): 2731 case XC( OC_TEST ):
2732 debug_printf_eval("TEST\n");
2658 if ((op1->info & OPCLSMASK) == OC_COMMA) { 2733 if ((op1->info & OPCLSMASK) == OC_COMMA) {
2659 /* it's range pattern */ 2734 /* it's range pattern */
2660 if ((opinfo & OF_CHECKED) || ptest(op1->l.n)) { 2735 if ((opinfo & OF_CHECKED) || ptest(op1->l.n)) {
@@ -2672,25 +2747,32 @@ static var *evaluate(node *op, var *res)
2672 2747
2673 /* just evaluate an expression, also used as unconditional jump */ 2748 /* just evaluate an expression, also used as unconditional jump */
2674 case XC( OC_EXEC ): 2749 case XC( OC_EXEC ):
2750 debug_printf_eval("EXEC\n");
2675 break; 2751 break;
2676 2752
2677 /* branch, used in if-else and various loops */ 2753 /* branch, used in if-else and various loops */
2678 case XC( OC_BR ): 2754 case XC( OC_BR ):
2755 debug_printf_eval("BR\n");
2679 op = istrue(L.v) ? op->a.n : op->r.n; 2756 op = istrue(L.v) ? op->a.n : op->r.n;
2680 break; 2757 break;
2681 2758
2682 /* initialize for-in loop */ 2759 /* initialize for-in loop */
2683 case XC( OC_WALKINIT ): 2760 case XC( OC_WALKINIT ):
2761 debug_printf_eval("WALKINIT\n");
2684 hashwalk_init(L.v, iamarray(R.v)); 2762 hashwalk_init(L.v, iamarray(R.v));
2685 break; 2763 break;
2686 2764
2687 /* get next array item */ 2765 /* get next array item */
2688 case XC( OC_WALKNEXT ): 2766 case XC( OC_WALKNEXT ):
2767 debug_printf_eval("WALKNEXT\n");
2689 op = hashwalk_next(L.v) ? op->a.n : op->r.n; 2768 op = hashwalk_next(L.v) ? op->a.n : op->r.n;
2690 break; 2769 break;
2691 2770
2692 case XC( OC_PRINT ): 2771 case XC( OC_PRINT ):
2693 case XC( OC_PRINTF ): { 2772 debug_printf_eval("PRINT /\n");
2773 case XC( OC_PRINTF ):
2774 debug_printf_eval("PRINTF\n");
2775 {
2694 FILE *F = stdout; 2776 FILE *F = stdout;
2695 IF_FEATURE_AWK_GNU_EXTENSIONS(int len;) 2777 IF_FEATURE_AWK_GNU_EXTENSIONS(int len;)
2696 2778
@@ -2745,22 +2827,28 @@ static var *evaluate(node *op, var *res)
2745 /* case XC( OC_DELETE ): - moved to happen before arg evaluation */ 2827 /* case XC( OC_DELETE ): - moved to happen before arg evaluation */
2746 2828
2747 case XC( OC_NEWSOURCE ): 2829 case XC( OC_NEWSOURCE ):
2830 debug_printf_eval("NEWSOURCE\n");
2748 g_progname = op->l.new_progname; 2831 g_progname = op->l.new_progname;
2749 break; 2832 break;
2750 2833
2751 case XC( OC_RETURN ): 2834 case XC( OC_RETURN ):
2835 debug_printf_eval("RETURN\n");
2752 copyvar(res, L.v); 2836 copyvar(res, L.v);
2753 break; 2837 break;
2754 2838
2755 case XC( OC_NEXTFILE ): 2839 case XC( OC_NEXTFILE ):
2840 debug_printf_eval("NEXTFILE\n");
2756 nextfile = TRUE; 2841 nextfile = TRUE;
2757 case XC( OC_NEXT ): 2842 case XC( OC_NEXT ):
2843 debug_printf_eval("NEXT\n");
2758 nextrec = TRUE; 2844 nextrec = TRUE;
2759 case XC( OC_DONE ): 2845 case XC( OC_DONE ):
2846 debug_printf_eval("DONE\n");
2760 clrvar(res); 2847 clrvar(res);
2761 break; 2848 break;
2762 2849
2763 case XC( OC_EXIT ): 2850 case XC( OC_EXIT ):
2851 debug_printf_eval("EXIT\n");
2764 awk_exit(L_d); 2852 awk_exit(L_d);
2765 2853
2766 /* -- recursive node type -- */ 2854 /* -- recursive node type -- */
@@ -2780,15 +2868,18 @@ static var *evaluate(node *op, var *res)
2780 break; 2868 break;
2781 2869
2782 case XC( OC_IN ): 2870 case XC( OC_IN ):
2871 debug_printf_eval("IN\n");
2783 setvar_i(res, hash_search(iamarray(R.v), L.s) ? 1 : 0); 2872 setvar_i(res, hash_search(iamarray(R.v), L.s) ? 1 : 0);
2784 break; 2873 break;
2785 2874
2786 case XC( OC_REGEXP ): 2875 case XC( OC_REGEXP ):
2876 debug_printf_eval("REGEXP\n");
2787 op1 = op; 2877 op1 = op;
2788 L.s = getvar_s(intvar[F0]); 2878 L.s = getvar_s(intvar[F0]);
2789 goto re_cont; 2879 goto re_cont;
2790 2880
2791 case XC( OC_MATCH ): 2881 case XC( OC_MATCH ):
2882 debug_printf_eval("MATCH\n");
2792 op1 = op->r.n; 2883 op1 = op->r.n;
2793 re_cont: 2884 re_cont:
2794 { 2885 {
@@ -2814,6 +2905,7 @@ static var *evaluate(node *op, var *res)
2814 break; 2905 break;
2815 2906
2816 case XC( OC_TERNARY ): 2907 case XC( OC_TERNARY ):
2908 debug_printf_eval("TERNARY\n");
2817 if ((op->r.n->info & OPCLSMASK) != OC_COLON) 2909 if ((op->r.n->info & OPCLSMASK) != OC_COLON)
2818 syntax_error(EMSG_POSSIBLE_ERROR); 2910 syntax_error(EMSG_POSSIBLE_ERROR);
2819 res = evaluate(istrue(L.v) ? op->r.n->l.n : op->r.n->r.n, res); 2911 res = evaluate(istrue(L.v) ? op->r.n->l.n : op->r.n->r.n, res);
@@ -2822,6 +2914,7 @@ static var *evaluate(node *op, var *res)
2822 case XC( OC_FUNC ): { 2914 case XC( OC_FUNC ): {
2823 var *vbeg, *v; 2915 var *vbeg, *v;
2824 const char *sv_progname; 2916 const char *sv_progname;
2917 debug_printf_eval("FUNC\n");
2825 2918
2826 /* The body might be empty, still has to eval the args */ 2919 /* The body might be empty, still has to eval the args */
2827 if (!op->r.n->info && !op->r.f->body.first) 2920 if (!op->r.n->info && !op->r.f->body.first)
@@ -2851,7 +2944,10 @@ static var *evaluate(node *op, var *res)
2851 } 2944 }
2852 2945
2853 case XC( OC_GETLINE ): 2946 case XC( OC_GETLINE ):
2854 case XC( OC_PGETLINE ): { 2947 debug_printf_eval("GETLINE /\n");
2948 case XC( OC_PGETLINE ):
2949 debug_printf_eval("PGETLINE\n");
2950 {
2855 rstream *rsm; 2951 rstream *rsm;
2856 int i; 2952 int i;
2857 2953
@@ -2892,6 +2988,7 @@ static var *evaluate(node *op, var *res)
2892 /* simple builtins */ 2988 /* simple builtins */
2893 case XC( OC_FBLTIN ): { 2989 case XC( OC_FBLTIN ): {
2894 double R_d = R_d; /* for compiler */ 2990 double R_d = R_d; /* for compiler */
2991 debug_printf_eval("FBLTIN\n");
2895 2992
2896 switch (opn) { 2993 switch (opn) {
2897 case F_in: 2994 case F_in:
@@ -3005,14 +3102,18 @@ static var *evaluate(node *op, var *res)
3005 } 3102 }
3006 3103
3007 case XC( OC_BUILTIN ): 3104 case XC( OC_BUILTIN ):
3105 debug_printf_eval("BUILTIN\n");
3008 res = exec_builtin(op, res); 3106 res = exec_builtin(op, res);
3009 break; 3107 break;
3010 3108
3011 case XC( OC_SPRINTF ): 3109 case XC( OC_SPRINTF ):
3110 debug_printf_eval("SPRINTF\n");
3012 setvar_p(res, awk_printf(op1, NULL)); 3111 setvar_p(res, awk_printf(op1, NULL));
3013 break; 3112 break;
3014 3113
3015 case XC( OC_UNARY ): { 3114 case XC( OC_UNARY ):
3115 debug_printf_eval("UNARY\n");
3116 {
3016 double Ld, R_d; 3117 double Ld, R_d;
3017 3118
3018 Ld = R_d = getvar_i(R.v); 3119 Ld = R_d = getvar_i(R.v);
@@ -3042,7 +3143,9 @@ static var *evaluate(node *op, var *res)
3042 break; 3143 break;
3043 } 3144 }
3044 3145
3045 case XC( OC_FIELD ): { 3146 case XC( OC_FIELD ):
3147 debug_printf_eval("FIELD\n");
3148 {
3046 int i = (int)getvar_i(R.v); 3149 int i = (int)getvar_i(R.v);
3047 if (i < 0) 3150 if (i < 0)
3048 syntax_error(EMSG_NEGATIVE_FIELD); 3151 syntax_error(EMSG_NEGATIVE_FIELD);
@@ -3059,8 +3162,10 @@ static var *evaluate(node *op, var *res)
3059 3162
3060 /* concatenation (" ") and index joining (",") */ 3163 /* concatenation (" ") and index joining (",") */
3061 case XC( OC_CONCAT ): 3164 case XC( OC_CONCAT ):
3165 debug_printf_eval("CONCAT /\n");
3062 case XC( OC_COMMA ): { 3166 case XC( OC_COMMA ): {
3063 const char *sep = ""; 3167 const char *sep = "";
3168 debug_printf_eval("COMMA\n");
3064 if ((opinfo & OPCLSMASK) == OC_COMMA) 3169 if ((opinfo & OPCLSMASK) == OC_COMMA)
3065 sep = getvar_s(intvar[SUBSEP]); 3170 sep = getvar_s(intvar[SUBSEP]);
3066 setvar_p(res, xasprintf("%s%s%s", L.s, sep, R.s)); 3171 setvar_p(res, xasprintf("%s%s%s", L.s, sep, R.s));
@@ -3068,17 +3173,22 @@ static var *evaluate(node *op, var *res)
3068 } 3173 }
3069 3174
3070 case XC( OC_LAND ): 3175 case XC( OC_LAND ):
3176 debug_printf_eval("LAND\n");
3071 setvar_i(res, istrue(L.v) ? ptest(op->r.n) : 0); 3177 setvar_i(res, istrue(L.v) ? ptest(op->r.n) : 0);
3072 break; 3178 break;
3073 3179
3074 case XC( OC_LOR ): 3180 case XC( OC_LOR ):
3181 debug_printf_eval("LOR\n");
3075 setvar_i(res, istrue(L.v) ? 1 : ptest(op->r.n)); 3182 setvar_i(res, istrue(L.v) ? 1 : ptest(op->r.n));
3076 break; 3183 break;
3077 3184
3078 case XC( OC_BINARY ): 3185 case XC( OC_BINARY ):
3079 case XC( OC_REPLACE ): { 3186 debug_printf_eval("BINARY /\n");
3187 case XC( OC_REPLACE ):
3188 debug_printf_eval("REPLACE\n");
3189 {
3080 double R_d = getvar_i(R.v); 3190 double R_d = getvar_i(R.v);
3081 debug_printf_eval("BINARY/REPLACE: R_d:%f opn:%c\n", R_d, opn); 3191 debug_printf_eval("R_d:%f opn:%c\n", R_d, opn);
3082 switch (opn) { 3192 switch (opn) {
3083 case '+': 3193 case '+':
3084 L_d += R_d; 3194 L_d += R_d;
@@ -3114,6 +3224,7 @@ static var *evaluate(node *op, var *res)
3114 case XC( OC_COMPARE ): { 3224 case XC( OC_COMPARE ): {
3115 int i = i; /* for compiler */ 3225 int i = i; /* for compiler */
3116 double Ld; 3226 double Ld;
3227 debug_printf_eval("COMPARE\n");
3117 3228
3118 if (is_numeric(L.v) && is_numeric(R.v)) { 3229 if (is_numeric(L.v) && is_numeric(R.v)) {
3119 Ld = getvar_i(L.v) - getvar_i(R.v); 3230 Ld = getvar_i(L.v) - getvar_i(R.v);
@@ -3162,20 +3273,19 @@ static var *evaluate(node *op, var *res)
3162 3273
3163static int awk_exit(int r) 3274static int awk_exit(int r)
3164{ 3275{
3165 var tv;
3166 unsigned i; 3276 unsigned i;
3167 hash_item *hi;
3168
3169 zero_out_var(&tv);
3170 3277
3171 if (!exiting) { 3278 if (!exiting) {
3279 var tv;
3172 exiting = TRUE; 3280 exiting = TRUE;
3173 nextrec = FALSE; 3281 nextrec = FALSE;
3282 zero_out_var(&tv);
3174 evaluate(endseq.first, &tv); 3283 evaluate(endseq.first, &tv);
3175 } 3284 }
3176 3285
3177 /* waiting for children */ 3286 /* waiting for children */
3178 for (i = 0; i < fdhash->csize; i++) { 3287 for (i = 0; i < fdhash->csize; i++) {
3288 hash_item *hi;
3179 hi = fdhash->items[i]; 3289 hi = fdhash->items[i];
3180 while (hi) { 3290 while (hi) {
3181 if (hi->data.rs.F && hi->data.rs.is_pipe) 3291 if (hi->data.rs.F && hi->data.rs.is_pipe)
@@ -3255,12 +3365,8 @@ int awk_main(int argc UNUSED_PARAM, char **argv)
3255#if ENABLE_FEATURE_AWK_GNU_EXTENSIONS 3365#if ENABLE_FEATURE_AWK_GNU_EXTENSIONS
3256 llist_t *list_e = NULL; 3366 llist_t *list_e = NULL;
3257#endif 3367#endif
3258 int i, j; 3368 int i;
3259 var *v;
3260 var tv; 3369 var tv;
3261 char **envp;
3262 char *vnames = (char *)vNames; /* cheat */
3263 char *vvalues = (char *)vValues;
3264 3370
3265 INIT_G(); 3371 INIT_G();
3266 3372
@@ -3269,8 +3375,6 @@ int awk_main(int argc UNUSED_PARAM, char **argv)
3269 if (ENABLE_LOCALE_SUPPORT) 3375 if (ENABLE_LOCALE_SUPPORT)
3270 setlocale(LC_NUMERIC, "C"); 3376 setlocale(LC_NUMERIC, "C");
3271 3377
3272 zero_out_var(&tv);
3273
3274 /* allocate global buffer */ 3378 /* allocate global buffer */
3275 g_buf = xmalloc(MAXVARFMT + 1); 3379 g_buf = xmalloc(MAXVARFMT + 1);
3276 3380
@@ -3280,16 +3384,21 @@ int awk_main(int argc UNUSED_PARAM, char **argv)
3280 fnhash = hash_init(); 3384 fnhash = hash_init();
3281 3385
3282 /* initialize variables */ 3386 /* initialize variables */
3283 for (i = 0; *vnames; i++) { 3387 {
3284 intvar[i] = v = newvar(nextword(&vnames)); 3388 char *vnames = (char *)vNames; /* cheat */
3285 if (*vvalues != '\377') 3389 char *vvalues = (char *)vValues;
3286 setvar_s(v, nextword(&vvalues)); 3390 for (i = 0; *vnames; i++) {
3287 else 3391 var *v;
3288 setvar_i(v, 0); 3392 intvar[i] = v = newvar(nextword(&vnames));
3289 3393 if (*vvalues != '\377')
3290 if (*vnames == '*') { 3394 setvar_s(v, nextword(&vvalues));
3291 v->type |= VF_SPECIAL; 3395 else
3292 vnames++; 3396 setvar_i(v, 0);
3397
3398 if (*vnames == '*') {
3399 v->type |= VF_SPECIAL;
3400 vnames++;
3401 }
3293 } 3402 }
3294 } 3403 }
3295 3404
@@ -3301,16 +3410,19 @@ int awk_main(int argc UNUSED_PARAM, char **argv)
3301 newfile("/dev/stderr")->F = stderr; 3410 newfile("/dev/stderr")->F = stderr;
3302 3411
3303 /* Huh, people report that sometimes environ is NULL. Oh well. */ 3412 /* Huh, people report that sometimes environ is NULL. Oh well. */
3304 if (environ) for (envp = environ; *envp; envp++) { 3413 if (environ) {
3305 /* environ is writable, thus we don't strdup it needlessly */ 3414 char **envp;
3306 char *s = *envp; 3415 for (envp = environ; *envp; envp++) {
3307 char *s1 = strchr(s, '='); 3416 /* environ is writable, thus we don't strdup it needlessly */
3308 if (s1) { 3417 char *s = *envp;
3309 *s1 = '\0'; 3418 char *s1 = strchr(s, '=');
3310 /* Both findvar and setvar_u take const char* 3419 if (s1) {
3311 * as 2nd arg -> environment is not trashed */ 3420 *s1 = '\0';
3312 setvar_u(findvar(iamarray(intvar[ENVIRON]), s), s1 + 1); 3421 /* Both findvar and setvar_u take const char*
3313 *s1 = '='; 3422 * as 2nd arg -> environment is not trashed */
3423 setvar_u(findvar(iamarray(intvar[ENVIRON]), s), s1 + 1);
3424 *s1 = '=';
3425 }
3314 } 3426 }
3315 } 3427 }
3316 opt = getopt32(argv, OPTSTR_AWK, &opt_F, &list_v, &list_f, IF_FEATURE_AWK_GNU_EXTENSIONS(&list_e,) NULL); 3428 opt = getopt32(argv, OPTSTR_AWK, &opt_F, &list_v, &list_f, IF_FEATURE_AWK_GNU_EXTENSIONS(&list_e,) NULL);
@@ -3327,30 +3439,43 @@ int awk_main(int argc UNUSED_PARAM, char **argv)
3327 bb_show_usage(); 3439 bb_show_usage();
3328 } 3440 }
3329 while (list_f) { 3441 while (list_f) {
3330 char *s = NULL; 3442 int fd;
3331 FILE *from_file; 3443 char *s;
3332 3444
3333 g_progname = llist_pop(&list_f); 3445 g_progname = llist_pop(&list_f);
3334 from_file = xfopen_stdin(g_progname); 3446 fd = xopen_stdin(g_progname);
3335 /* one byte is reserved for some trick in next_token */ 3447 /* 1st byte is reserved for "move name one char back" trick in next_token */
3336 for (i = j = 1; j > 0; i += j) { 3448 i = 1;
3337 s = xrealloc(s, i + 4096); 3449 s = NULL;
3338 j = fread(s + i, 1, 4094, from_file); 3450 for (;;) {
3451 int sz;
3452 s = xrealloc(s, i + 1000);
3453 sz = safe_read(fd, s + i, 1000);
3454 if (sz <= 0)
3455 break;
3456 i += sz;
3339 } 3457 }
3458 s = xrealloc(s, i + 1); /* trim unused 999 bytes */
3340 s[i] = '\0'; 3459 s[i] = '\0';
3341 fclose(from_file); 3460 close(fd);
3342 parse_program(s + 1); 3461 parse_program(s + 1);
3343 free(s); 3462 free(s);
3344 } 3463 }
3345 g_progname = "cmd. line"; 3464 g_progname = "cmd. line";
3346#if ENABLE_FEATURE_AWK_GNU_EXTENSIONS 3465#if ENABLE_FEATURE_AWK_GNU_EXTENSIONS
3347 while (list_e) { 3466 while (list_e) {
3467 /* NB: "move name one char back" trick in next_token
3468 * can use argv[i][-1] here.
3469 */
3348 parse_program(llist_pop(&list_e)); 3470 parse_program(llist_pop(&list_e));
3349 } 3471 }
3350#endif 3472#endif
3351 if (!(opt & (OPT_f | OPT_e))) { 3473 if (!(opt & (OPT_f | OPT_e))) {
3352 if (!*argv) 3474 if (!*argv)
3353 bb_show_usage(); 3475 bb_show_usage();
3476 /* NB: "move name one char back" trick in next_token
3477 * can use argv[i][-1] here.
3478 */
3354 parse_program(*argv++); 3479 parse_program(*argv++);
3355 } 3480 }
3356 3481
@@ -3361,6 +3486,7 @@ int awk_main(int argc UNUSED_PARAM, char **argv)
3361 setari_u(intvar[ARGV], ++i, *argv++); 3486 setari_u(intvar[ARGV], ++i, *argv++);
3362 setvar_i(intvar[ARGC], i + 1); 3487 setvar_i(intvar[ARGC], i + 1);
3363 3488
3489 zero_out_var(&tv);
3364 evaluate(beginseq.first, &tv); 3490 evaluate(beginseq.first, &tv);
3365 if (!mainseq.first && !endseq.first) 3491 if (!mainseq.first && !endseq.first)
3366 awk_exit(EXIT_SUCCESS); 3492 awk_exit(EXIT_SUCCESS);
diff --git a/editors/cmp.c b/editors/cmp.c
index 6e27a841a..e106d814e 100644
--- a/editors/cmp.c
+++ b/editors/cmp.c
@@ -18,7 +18,7 @@
18//kbuild:lib-$(CONFIG_CMP) += cmp.o 18//kbuild:lib-$(CONFIG_CMP) += cmp.o
19 19
20//usage:#define cmp_trivial_usage 20//usage:#define cmp_trivial_usage
21//usage: "[-l] [-s] FILE1 [FILE2" IF_DESKTOP(" [SKIP1 [SKIP2]]") "]" 21//usage: "[-ls] FILE1 [FILE2" IF_DESKTOP(" [SKIP1 [SKIP2]]") "]"
22//usage:#define cmp_full_usage "\n\n" 22//usage:#define cmp_full_usage "\n\n"
23//usage: "Compare FILE1 with FILE2 (or stdin)\n" 23//usage: "Compare FILE1 with FILE2 (or stdin)\n"
24//usage: "\n -l Write the byte numbers (decimal) and values (octal)" 24//usage: "\n -l Write the byte numbers (decimal) and values (octal)"
diff --git a/editors/sed.c b/editors/sed.c
index b269b58d8..523fb8dba 100644
--- a/editors/sed.c
+++ b/editors/sed.c
@@ -70,7 +70,7 @@
70//usage:#define sed_full_usage "\n\n" 70//usage:#define sed_full_usage "\n\n"
71//usage: " -e CMD Add CMD to sed commands to be executed" 71//usage: " -e CMD Add CMD to sed commands to be executed"
72//usage: "\n -f FILE Add FILE contents to sed commands to be executed" 72//usage: "\n -f FILE Add FILE contents to sed commands to be executed"
73//usage: "\n -i[SFX] Edit files in-place (otherwise sends to stdout)" 73//usage: "\n -i[SFX] Edit files in-place (otherwise write to stdout)"
74//usage: "\n Optionally back files up, appending SFX" 74//usage: "\n Optionally back files up, appending SFX"
75//usage: "\n -n Suppress automatic printing of pattern space" 75//usage: "\n -n Suppress automatic printing of pattern space"
76//usage: "\n -r,-E Use extended regex syntax" 76//usage: "\n -r,-E Use extended regex syntax"
diff --git a/editors/vi.c b/editors/vi.c
index a4d6b21b4..89c567f10 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -181,7 +181,7 @@
181//kbuild:lib-$(CONFIG_VI) += vi.o 181//kbuild:lib-$(CONFIG_VI) += vi.o
182 182
183//usage:#define vi_trivial_usage 183//usage:#define vi_trivial_usage
184//usage: IF_FEATURE_VI_COLON("[-c CMD] ")IF_FEATURE_VI_READONLY("[-R] ")"[FILE]..." 184//usage: IF_FEATURE_VI_COLON("[-c CMD] ")IF_FEATURE_VI_READONLY("[-R] ")"[-H] [FILE]..."
185//usage:#define vi_full_usage "\n\n" 185//usage:#define vi_full_usage "\n\n"
186//usage: "Edit FILE\n" 186//usage: "Edit FILE\n"
187//usage: IF_FEATURE_VI_COLON( 187//usage: IF_FEATURE_VI_COLON(
@@ -191,6 +191,7 @@
191//usage: "\n -R Read-only" 191//usage: "\n -R Read-only"
192//usage: ) 192//usage: )
193//usage: "\n -H List available features" 193//usage: "\n -H List available features"
194// note: non-standard, "vim -H" is Hebrew mode (bidi support)
194 195
195#include "libbb.h" 196#include "libbb.h"
196// Should be after libbb.h: on some systems regex.h needs sys/types.h: 197// Should be after libbb.h: on some systems regex.h needs sys/types.h:
@@ -377,6 +378,7 @@ struct globals {
377#if ENABLE_FEATURE_VI_SETOPTS 378#if ENABLE_FEATURE_VI_SETOPTS
378 int indentcol; // column of recently autoindent, 0 or -1 379 int indentcol; // column of recently autoindent, 0 or -1
379#endif 380#endif
381 smallint cmd_error;
380 382
381 // former statics 383 // former statics
382#if ENABLE_FEATURE_VI_YANKMARK 384#if ENABLE_FEATURE_VI_YANKMARK
@@ -503,6 +505,7 @@ struct globals {
503#define dotcnt (G.dotcnt ) 505#define dotcnt (G.dotcnt )
504#define last_search_pattern (G.last_search_pattern) 506#define last_search_pattern (G.last_search_pattern)
505#define indentcol (G.indentcol ) 507#define indentcol (G.indentcol )
508#define cmd_error (G.cmd_error )
506 509
507#define edit_file__cur_line (G.edit_file__cur_line) 510#define edit_file__cur_line (G.edit_file__cur_line)
508#define refresh__old_offset (G.refresh__old_offset) 511#define refresh__old_offset (G.refresh__old_offset)
@@ -537,6 +540,7 @@ struct globals {
537 last_modified_count = -1; \ 540 last_modified_count = -1; \
538 /* "" but has space for 2 chars: */ \ 541 /* "" but has space for 2 chars: */ \
539 IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \ 542 IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \
543 tabstop = 8; \
540} while (0) 544} while (0)
541 545
542#if ENABLE_FEATURE_VI_CRASHME 546#if ENABLE_FEATURE_VI_CRASHME
@@ -1122,6 +1126,7 @@ static void indicate_error(void)
1122 if (crashme > 0) 1126 if (crashme > 0)
1123 return; 1127 return;
1124#endif 1128#endif
1129 cmd_error = TRUE;
1125 if (!err_method) { 1130 if (!err_method) {
1126 write1(ESC_BELL); 1131 write1(ESC_BELL);
1127 } else { 1132 } else {
@@ -2191,13 +2196,13 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2191 cmdcnt = 0; 2196 cmdcnt = 0;
2192 end_cmd_q(); // stop adding to q 2197 end_cmd_q(); // stop adding to q
2193 last_status_cksum = 0; // force status update 2198 last_status_cksum = 0; // force status update
2194 if ((p[-1] != '\n') && (dot > text)) { 2199 if ((dot > text) && (p[-1] != '\n')) {
2195 p--; 2200 p--;
2196 } 2201 }
2197#if ENABLE_FEATURE_VI_SETOPTS 2202#if ENABLE_FEATURE_VI_SETOPTS
2198 if (autoindent) { 2203 if (autoindent) {
2199 len = indent_len(bol); 2204 len = indent_len(bol);
2200 if (len && get_column(bol + len) == indentcol) { 2205 if (len && get_column(bol + len) == indentcol && bol[len] == '\n') {
2201 // remove autoindent from otherwise empty line 2206 // remove autoindent from otherwise empty line
2202 text_hole_delete(bol, bol + len - 1, undo); 2207 text_hole_delete(bol, bol + len - 1, undo);
2203 p = bol; 2208 p = bol;
@@ -2437,9 +2442,7 @@ static char *char_search(char *p, const char *pat, int dir_and_range)
2437 struct re_pattern_buffer preg; 2442 struct re_pattern_buffer preg;
2438 const char *err; 2443 const char *err;
2439 char *q; 2444 char *q;
2440 int i; 2445 int i, size, range, start;
2441 int size;
2442 int range;
2443 2446
2444 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED; 2447 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
2445 if (ignorecase) 2448 if (ignorecase)
@@ -2464,31 +2467,26 @@ static char *char_search(char *p, const char *pat, int dir_and_range)
2464 2467
2465 // RANGE could be negative if we are searching backwards 2468 // RANGE could be negative if we are searching backwards
2466 range = q - p; 2469 range = q - p;
2467 q = p;
2468 size = range;
2469 if (range < 0) { 2470 if (range < 0) {
2470 size = -size; 2471 size = -range;
2471 q = p - size; 2472 start = size;
2472 if (q < text) 2473 } else {
2473 q = text; 2474 size = range;
2475 start = 0;
2474 } 2476 }
2477 q = p - start;
2478 if (q < text)
2479 q = text;
2475 // search for the compiled pattern, preg, in p[] 2480 // search for the compiled pattern, preg, in p[]
2476 // range < 0: search backward 2481 // range < 0, start == size: search backward
2477 // range > 0: search forward 2482 // range > 0, start == 0: search forward
2478 // 0 < start < size
2479 // re_search() < 0: not found or error 2483 // re_search() < 0: not found or error
2480 // re_search() >= 0: index of found pattern 2484 // re_search() >= 0: index of found pattern
2481 // struct pattern char int int int struct reg 2485 // struct pattern char int int int struct reg
2482 // re_search(*pattern_buffer, *string, size, start, range, *regs) 2486 // re_search(*pattern_buffer, *string, size, start, range, *regs)
2483 i = re_search(&preg, q, size, /*start:*/ 0, range, /*struct re_registers*:*/ NULL); 2487 i = re_search(&preg, q, size, start, range, /*struct re_registers*:*/ NULL);
2484 regfree(&preg); 2488 regfree(&preg);
2485 if (i < 0) 2489 return i < 0 ? NULL : q + i;
2486 return NULL;
2487 if (dir_and_range > 0) // FORWARD?
2488 p = p + i;
2489 else
2490 p = p - i;
2491 return p;
2492} 2490}
2493# else 2491# else
2494# if ENABLE_FEATURE_VI_SETOPTS 2492# if ENABLE_FEATURE_VI_SETOPTS
@@ -3063,12 +3061,10 @@ static void colon(char *buf)
3063 status_line_bold("No current filename"); 3061 status_line_bold("No current filename");
3064 goto ret; 3062 goto ret;
3065 } 3063 }
3066 if (e < 0) { // no addr given- read after current line 3064 if (e == 0) { // user said ":0r foo"
3067 q = begin_line(dot);
3068 } else if (e == 0) { // user said ":0r foo"
3069 q = text; 3065 q = text;
3070 } else { // addr given- read after that line 3066 } else { // read after given line or current line if none given
3071 q = next_line(find_line(e)); 3067 q = next_line(e > 0 ? find_line(e) : dot);
3072 // read after last line 3068 // read after last line
3073 if (q == end-1) 3069 if (q == end-1)
3074 ++q; 3070 ++q;
@@ -3170,6 +3166,18 @@ static void colon(char *buf)
3170 } 3166 }
3171 len_R = strlen(R); 3167 len_R = strlen(R);
3172 3168
3169 if (len_F) { // save "find" as last search pattern
3170 free(last_search_pattern);
3171 last_search_pattern = xstrdup(F - 1);
3172 last_search_pattern[0] = '/';
3173 } else if (last_search_pattern[1] == '\0') {
3174 status_line_bold("No previous search");
3175 goto ret;
3176 } else {
3177 F = last_search_pattern + 1;
3178 len_F = strlen(F);
3179 }
3180
3173 if (e < 0) { // no addr given 3181 if (e < 0) { // no addr given
3174 q = begin_line(dot); // start with cur line 3182 q = begin_line(dot); // start with cur line
3175 r = end_line(dot); 3183 r = end_line(dot);
@@ -3465,8 +3473,11 @@ static int find_range(char **start, char **stop, int cmd)
3465#endif 3473#endif
3466 // these cmds operate on whole lines 3474 // these cmds operate on whole lines
3467 buftype = WHOLE; 3475 buftype = WHOLE;
3468 if (--cmdcnt > 0) 3476 if (--cmdcnt > 0) {
3469 do_cmd('j'); 3477 do_cmd('j');
3478 if (cmd_error)
3479 buftype = -1;
3480 }
3470 } else if (strchr("^%$0bBeEfFtThnN/?|{}\b\177", c)) { 3481 } else if (strchr("^%$0bBeEfFtThnN/?|{}\b\177", c)) {
3471 // Most operate on char positions within a line. Of those that 3482 // Most operate on char positions within a line. Of those that
3472 // don't '%' needs no special treatment, search commands are 3483 // don't '%' needs no special treatment, search commands are
@@ -3496,6 +3507,8 @@ static int find_range(char **start, char **stop, int cmd)
3496 // these operate on whole lines 3507 // these operate on whole lines
3497 buftype = WHOLE; 3508 buftype = WHOLE;
3498 do_cmd(c); // execute movement cmd 3509 do_cmd(c); // execute movement cmd
3510 if (cmd_error)
3511 buftype = -1;
3499 } else if (c == ' ' || c == 'l') { 3512 } else if (c == ' ' || c == 'l') {
3500 // forward motion by character 3513 // forward motion by character
3501 int tmpcnt = (cmdcnt ?: 1); 3514 int tmpcnt = (cmdcnt ?: 1);
@@ -3581,6 +3594,7 @@ static void do_cmd(int c)
3581// p = q = save_dot = buf; // quiet the compiler 3594// p = q = save_dot = buf; // quiet the compiler
3582 memset(buf, '\0', sizeof(buf)); 3595 memset(buf, '\0', sizeof(buf));
3583 keep_index = FALSE; 3596 keep_index = FALSE;
3597 cmd_error = FALSE;
3584 3598
3585 show_status_line(); 3599 show_status_line();
3586 3600
@@ -3701,24 +3715,30 @@ static void do_cmd(int c)
3701 case 10: // Newline ^J 3715 case 10: // Newline ^J
3702 case 'j': // j- goto next line, same col 3716 case 'j': // j- goto next line, same col
3703 case KEYCODE_DOWN: // cursor key Down 3717 case KEYCODE_DOWN: // cursor key Down
3718 case 13: // Carriage Return ^M
3719 case '+': // +- goto next line
3720 q = dot;
3704 do { 3721 do {
3705 dot_next(); // go to next B-o-l 3722 p = next_line(q);
3723 if (p == end_line(q)) {
3724 indicate_error();
3725 goto dc1;
3726 }
3727 q = p;
3706 } while (--cmdcnt > 0); 3728 } while (--cmdcnt > 0);
3707 // try to stay in saved column 3729 dot = q;
3708 dot = cindex == C_END ? end_line(dot) : move_to_col(dot, cindex); 3730 if (c == 13 || c == '+') {
3709 keep_index = TRUE; 3731 dot_skip_over_ws();
3732 } else {
3733 // try to stay in saved column
3734 dot = cindex == C_END ? end_line(dot) : move_to_col(dot, cindex);
3735 keep_index = TRUE;
3736 }
3710 break; 3737 break;
3711 case 12: // ctrl-L force redraw whole screen 3738 case 12: // ctrl-L force redraw whole screen
3712 case 18: // ctrl-R force redraw 3739 case 18: // ctrl-R force redraw
3713 redraw(TRUE); // this will redraw the entire display 3740 redraw(TRUE); // this will redraw the entire display
3714 break; 3741 break;
3715 case 13: // Carriage Return ^M
3716 case '+': // +- goto next line
3717 do {
3718 dot_next();
3719 } while (--cmdcnt > 0);
3720 dot_skip_over_ws();
3721 break;
3722 case 21: // ctrl-U scroll up half screen 3742 case 21: // ctrl-U scroll up half screen
3723 dot_scroll((rows - 2) / 2, -1); 3743 dot_scroll((rows - 2) / 2, -1);
3724 break; 3744 break;
@@ -3759,6 +3779,8 @@ static void do_cmd(int c)
3759 dot = q; 3779 dot = q;
3760 dot_begin(); // go to B-o-l 3780 dot_begin(); // go to B-o-l
3761 dot_skip_over_ws(); 3781 dot_skip_over_ws();
3782 } else {
3783 indicate_error();
3762 } 3784 }
3763 } else if (c1 == '\'') { // goto previous context 3785 } else if (c1 == '\'') { // goto previous context
3764 dot = swap_context(dot); // swap current and previous context 3786 dot = swap_context(dot); // swap current and previous context
@@ -3884,12 +3906,6 @@ static void do_cmd(int c)
3884 case ',': // ,- repeat latest search in opposite direction 3906 case ',': // ,- repeat latest search in opposite direction
3885 dot_to_char(c != ',' ? last_search_cmd : last_search_cmd ^ 0x20); 3907 dot_to_char(c != ',' ? last_search_cmd : last_search_cmd ^ 0x20);
3886 break; 3908 break;
3887 case '-': // -- goto prev line
3888 do {
3889 dot_prev();
3890 } while (--cmdcnt > 0);
3891 dot_skip_over_ws();
3892 break;
3893#if ENABLE_FEATURE_VI_DOT_CMD 3909#if ENABLE_FEATURE_VI_DOT_CMD
3894 case '.': // .- repeat the last modifying command 3910 case '.': // .- repeat the last modifying command
3895 // Stuff the last_modifying_cmd back into stdin 3911 // Stuff the last_modifying_cmd back into stdin
@@ -4095,9 +4111,10 @@ static void do_cmd(int c)
4095 if (cmdcnt > (rows - 1)) { 4111 if (cmdcnt > (rows - 1)) {
4096 cmdcnt = (rows - 1); 4112 cmdcnt = (rows - 1);
4097 } 4113 }
4098 if (--cmdcnt > 0) { 4114 while (--cmdcnt > 0) {
4099 do_cmd('+'); 4115 dot_next();
4100 } 4116 }
4117 dot_begin();
4101 dot_skip_over_ws(); 4118 dot_skip_over_ws();
4102 break; 4119 break;
4103 case 'I': // I- insert before first non-blank 4120 case 'I': // I- insert before first non-blank
@@ -4134,8 +4151,8 @@ static void do_cmd(int c)
4134 if (cmdcnt > (rows - 1)) { 4151 if (cmdcnt > (rows - 1)) {
4135 cmdcnt = (rows - 1); 4152 cmdcnt = (rows - 1);
4136 } 4153 }
4137 if (--cmdcnt > 0) { 4154 while (--cmdcnt > 0) {
4138 do_cmd('-'); 4155 dot_prev();
4139 } 4156 }
4140 dot_begin(); 4157 dot_begin();
4141 dot_skip_over_ws(); 4158 dot_skip_over_ws();
@@ -4300,12 +4317,24 @@ static void do_cmd(int c)
4300 } 4317 }
4301 case 'k': // k- goto prev line, same col 4318 case 'k': // k- goto prev line, same col
4302 case KEYCODE_UP: // cursor key Up 4319 case KEYCODE_UP: // cursor key Up
4320 case '-': // -- goto prev line
4321 q = dot;
4303 do { 4322 do {
4304 dot_prev(); 4323 p = prev_line(q);
4324 if (p == begin_line(q)) {
4325 indicate_error();
4326 goto dc1;
4327 }
4328 q = p;
4305 } while (--cmdcnt > 0); 4329 } while (--cmdcnt > 0);
4306 // try to stay in saved column 4330 dot = q;
4307 dot = cindex == C_END ? end_line(dot) : move_to_col(dot, cindex); 4331 if (c == '-') {
4308 keep_index = TRUE; 4332 dot_skip_over_ws();
4333 } else {
4334 // try to stay in saved column
4335 dot = cindex == C_END ? end_line(dot) : move_to_col(dot, cindex);
4336 keep_index = TRUE;
4337 }
4309 break; 4338 break;
4310 case 'r': // r- replace the current char with user input 4339 case 'r': // r- replace the current char with user input
4311 c1 = get_one_char(); // get the replacement char 4340 c1 = get_one_char(); // get the replacement char
@@ -4686,7 +4715,6 @@ static void edit_file(char *fn)
4686 4715
4687 cmd_mode = 0; // 0=command 1=insert 2='R'eplace 4716 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
4688 cmdcnt = 0; 4717 cmdcnt = 0;
4689 tabstop = 8;
4690 offset = 0; // no horizontal offset 4718 offset = 0; // no horizontal offset
4691 c = '\0'; 4719 c = '\0';
4692#if ENABLE_FEATURE_VI_DOT_CMD 4720#if ENABLE_FEATURE_VI_DOT_CMD
@@ -4814,7 +4842,11 @@ int vi_main(int argc, char **argv)
4814 initial_cmds[0] = xstrndup(p, MAX_INPUT_LEN); 4842 initial_cmds[0] = xstrndup(p, MAX_INPUT_LEN);
4815 } 4843 }
4816#endif 4844#endif
4817 while ((c = getopt(argc, argv, "hCRH" IF_FEATURE_VI_COLON("c:"))) != -1) { 4845 while ((c = getopt(argc, argv,
4846#if ENABLE_FEATURE_VI_CRASHME
4847 "C"
4848#endif
4849 "RHh" IF_FEATURE_VI_COLON("c:"))) != -1) {
4818 switch (c) { 4850 switch (c) {
4819#if ENABLE_FEATURE_VI_CRASHME 4851#if ENABLE_FEATURE_VI_CRASHME
4820 case 'C': 4852 case 'C':
diff --git a/findutils/grep.c b/findutils/grep.c
index 10cca83e7..be4362ed0 100644
--- a/findutils/grep.c
+++ b/findutils/grep.c
@@ -57,14 +57,12 @@
57#include "common_bufsiz.h" 57#include "common_bufsiz.h"
58#include "xregex.h" 58#include "xregex.h"
59 59
60
61/* options */
62//usage:#define grep_trivial_usage 60//usage:#define grep_trivial_usage
63//usage: "[-HhnlLoqvsrRiwFE" 61//usage: "[-HhnlLoqvsrRiwFE"
64//usage: IF_EXTRA_COMPAT("z") 62//usage: IF_EXTRA_COMPAT("z")
65//usage: "] [-m N] " 63//usage: "] [-m N] "
66//usage: IF_FEATURE_GREP_CONTEXT("[-A/B/C N] ") 64//usage: IF_FEATURE_GREP_CONTEXT("[-A|B|C N] ")
67//usage: "PATTERN/-e PATTERN.../-f FILE [FILE]..." 65//usage: "{ PATTERN | -e PATTERN... | -f FILE... } [FILE]..."
68//usage:#define grep_full_usage "\n\n" 66//usage:#define grep_full_usage "\n\n"
69//usage: "Search for PATTERN in FILEs (or stdin)\n" 67//usage: "Search for PATTERN in FILEs (or stdin)\n"
70//usage: "\n -H Add 'filename:' prefix" 68//usage: "\n -H Add 'filename:' prefix"
diff --git a/include/bb_e2fs_defs.h b/include/bb_e2fs_defs.h
index 3f5e3c45b..608cbf0b0 100644
--- a/include/bb_e2fs_defs.h
+++ b/include/bb_e2fs_defs.h
@@ -182,11 +182,12 @@ struct ext2_dx_countlimit {
182#define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */ 182#define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */
183#define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */ 183#define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */
184#define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ 184#define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/
185#define EXT3_EXTENTS_FL 0x00080000 /* Inode uses extents */ 185#define EXT2_EXTENT_FL 0x00080000 /* Extents */
186#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ 186#define EXT2_VERITY_FL 0x00100000
187 187#define EXT2_NOCOW_FL 0x00800000 /* Do not cow file */
188#define EXT2_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */ 188#define EXT2_INLINE_DATA_FL 0x10000000
189#define EXT2_FL_USER_MODIFIABLE 0x000080FF /* User modifiable flags */ 189#define EXT2_PROJINHERIT_FL 0x20000000
190#define EXT2_CASEFOLD_FL 0x40000000
190 191
191/* 192/*
192 * ioctl commands 193 * ioctl commands
@@ -195,6 +196,18 @@ struct ext2_dx_countlimit {
195#define EXT2_IOC_SETFLAGS _IOW('f', 2, long) 196#define EXT2_IOC_SETFLAGS _IOW('f', 2, long)
196#define EXT2_IOC_GETVERSION _IOR('v', 1, long) 197#define EXT2_IOC_GETVERSION _IOR('v', 1, long)
197#define EXT2_IOC_SETVERSION _IOW('v', 2, long) 198#define EXT2_IOC_SETVERSION _IOW('v', 2, long)
199//NB: despite "long" in defs above, these ioctls use an _int_!
200//passing them a pointer to long will read/write only int-sized data!
201struct ext2_fsxattr {
202 uint32_t fsx_xflags; /* xflags field value (get/set) */
203 uint32_t fsx_extsize; /* extsize field value (get/set)*/
204 uint32_t fsx_nextents; /* nextents field value (get) */
205 uint32_t fsx_projid; /* project identifier (get/set) */
206 uint32_t fsx_cowextsize; /* CoW extsize field value (get/set)*/
207 unsigned char fsx_pad[8];
208};
209#define EXT2_IOC_FSGETXATTR _IOR('X', 31, struct ext2_fsxattr)
210#define EXT2_IOC_FSSETXATTR _IOW('X', 32, struct ext2_fsxattr)
198 211
199/* 212/*
200 * Structure of an inode on the disk 213 * Structure of an inode on the disk
diff --git a/include/dump.h b/include/dump.h
index 9193a6925..10fc5d900 100644
--- a/include/dump.h
+++ b/include/dump.h
@@ -32,8 +32,10 @@ typedef struct dumper_t {
32 off_t dump_skip; /* bytes to skip */ 32 off_t dump_skip; /* bytes to skip */
33 int dump_length; /* max bytes to read */ 33 int dump_length; /* max bytes to read */
34 smallint dump_vflag; /*enum dump_vflag_t*/ 34 smallint dump_vflag; /*enum dump_vflag_t*/
35 const char *eofstring;
36 FS *fshead; 35 FS *fshead;
36 const char *xxd_eofstring;
37 off_t address; /* address/offset in stream */
38 long long xxd_displayoff;
37} dumper_t; 39} dumper_t;
38 40
39dumper_t* alloc_dumper(void) FAST_FUNC; 41dumper_t* alloc_dumper(void) FAST_FUNC;
diff --git a/include/libbb.h b/include/libbb.h
index e80ed1e32..7a43967cb 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -469,23 +469,29 @@ enum { /* cp.c, mv.c, install.c depend on these values. CAREFUL when changing th
469 FILEUTILS_RECUR = 1 << 2, /* -R */ 469 FILEUTILS_RECUR = 1 << 2, /* -R */
470 FILEUTILS_FORCE = 1 << 3, /* -f */ 470 FILEUTILS_FORCE = 1 << 3, /* -f */
471 FILEUTILS_INTERACTIVE = 1 << 4, /* -i */ 471 FILEUTILS_INTERACTIVE = 1 << 4, /* -i */
472 FILEUTILS_MAKE_HARDLINK = 1 << 5, /* -l */ 472 FILEUTILS_NO_OVERWRITE = 1 << 5, /* -n */
473 FILEUTILS_MAKE_SOFTLINK = 1 << 6, /* -s */ 473 FILEUTILS_MAKE_HARDLINK = 1 << 6, /* -l */
474 FILEUTILS_DEREF_SOFTLINK = 1 << 7, /* -L */ 474 FILEUTILS_MAKE_SOFTLINK = 1 << 7, /* -s */
475 FILEUTILS_DEREFERENCE_L0 = 1 << 8, /* -H */ 475 FILEUTILS_DEREF_SOFTLINK = 1 << 8, /* -L */
476 FILEUTILS_DEREFERENCE_L0 = 1 << 9, /* -H */
476 /* -a = -pdR (mapped in cp.c) */ 477 /* -a = -pdR (mapped in cp.c) */
477 /* -r = -dR (mapped in cp.c) */ 478 /* -r = -dR (mapped in cp.c) */
478 /* -P = -d (mapped in cp.c) */ 479 /* -P = -d (mapped in cp.c) */
479 FILEUTILS_VERBOSE = (1 << 12) * ENABLE_FEATURE_VERBOSE, /* -v */ 480 FILEUTILS_VERBOSE = (1 << 13) * ENABLE_FEATURE_VERBOSE, /* -v */
480 FILEUTILS_UPDATE = 1 << 13, /* -u */ 481 FILEUTILS_UPDATE = 1 << 14, /* -u */
481 FILEUTILS_NO_TARGET_DIR = 1 << 14, /* -T */ 482 FILEUTILS_NO_TARGET_DIR = 1 << 15, /* -T */
483 FILEUTILS_TARGET_DIR = 1 << 16, /* -t DIR */
482#if ENABLE_SELINUX 484#if ENABLE_SELINUX
483 FILEUTILS_PRESERVE_SECURITY_CONTEXT = 1 << 15, /* -c */ 485 FILEUTILS_PRESERVE_SECURITY_CONTEXT = 1 << 17, /* -c */
484#endif 486#endif
485 FILEUTILS_RMDEST = 1 << (16 - !ENABLE_SELINUX), /* --remove-destination */ 487#define FILEUTILS_CP_OPTSTR "pdRfinlsLHarPvuTt:" IF_SELINUX("c")
486 /* bit 17 skipped for "cp --parents" */ 488/* How many bits in FILEUTILS_CP_OPTSTR? */
487 FILEUTILS_REFLINK = 1 << (18 - !ENABLE_SELINUX), /* cp --reflink=auto */ 489 FILEUTILS_CP_OPTBITS = 18 - !ENABLE_SELINUX,
488 FILEUTILS_REFLINK_ALWAYS = 1 << (19 - !ENABLE_SELINUX), /* cp --reflink[=always] */ 490
491 FILEUTILS_RMDEST = 1 << (19 - !ENABLE_SELINUX), /* cp --remove-destination */
492 /* bit 18 skipped for "cp --parents" */
493 FILEUTILS_REFLINK = 1 << (20 - !ENABLE_SELINUX), /* cp --reflink=auto */
494 FILEUTILS_REFLINK_ALWAYS = 1 << (21 - !ENABLE_SELINUX), /* cp --reflink[=always] */
489 /* 495 /*
490 * Hole. cp may have some bits set here, 496 * Hole. cp may have some bits set here,
491 * they should not affect remove_file()/copy_file() 497 * they should not affect remove_file()/copy_file()
@@ -495,7 +501,7 @@ enum { /* cp.c, mv.c, install.c depend on these values. CAREFUL when changing th
495#endif 501#endif
496 FILEUTILS_IGNORE_CHMOD_ERR = 1 << 31, 502 FILEUTILS_IGNORE_CHMOD_ERR = 1 << 31,
497}; 503};
498#define FILEUTILS_CP_OPTSTR "pdRfilsLHarPvuT" IF_SELINUX("c") 504
499extern int remove_file(const char *path, int flags) FAST_FUNC; 505extern int remove_file(const char *path, int flags) FAST_FUNC;
500/* NB: without FILEUTILS_RECUR in flags, it will basically "cat" 506/* NB: without FILEUTILS_RECUR in flags, it will basically "cat"
501 * the source, not copy (unless "source" is a directory). 507 * the source, not copy (unless "source" is a directory).
@@ -525,6 +531,11 @@ int recursive_action(const char *fileName, unsigned flags,
525 void *userData 531 void *userData
526) FAST_FUNC; 532) FAST_FUNC;
527 533
534/* Simpler version: call a function on each dirent in a directory */
535int iterate_on_dir(const char *dir_name,
536 int FAST_FUNC (*func)(const char *, struct dirent *, void *),
537 void *private) FAST_FUNC;
538
528extern int device_open(const char *device, int mode) FAST_FUNC; 539extern int device_open(const char *device, int mode) FAST_FUNC;
529enum { GETPTY_BUFSIZE = 16 }; /* more than enough for "/dev/ttyXXX" */ 540enum { GETPTY_BUFSIZE = 16 }; /* more than enough for "/dev/ttyXXX" */
530extern int xgetpty(char *line) FAST_FUNC; 541extern int xgetpty(char *line) FAST_FUNC;
@@ -1105,10 +1116,10 @@ char *smart_ulltoa5(unsigned long long ul, char buf[5], const char *scale) FAST_
1105/* If block_size == 0, display size without fractional part, 1116/* If block_size == 0, display size without fractional part,
1106 * else display (size * block_size) with one decimal digit. 1117 * else display (size * block_size) with one decimal digit.
1107 * If display_unit == 0, show value no bigger than 1024 with suffix (K,M,G...), 1118 * If display_unit == 0, show value no bigger than 1024 with suffix (K,M,G...),
1108 * else divide by display_unit and do not use suffix. */ 1119 * else divide by display_unit and do not use suffix.
1120 * Returns "auto pointer" */
1109#define HUMAN_READABLE_MAX_WIDTH 7 /* "1024.0G" */ 1121#define HUMAN_READABLE_MAX_WIDTH 7 /* "1024.0G" */
1110#define HUMAN_READABLE_MAX_WIDTH_STR "7" 1122#define HUMAN_READABLE_MAX_WIDTH_STR "7"
1111//TODO: provide pointer to buf (avoid statics)?
1112const char *make_human_readable_str(unsigned long long size, 1123const char *make_human_readable_str(unsigned long long size,
1113 unsigned long block_size, unsigned long display_unit) FAST_FUNC; 1124 unsigned long block_size, unsigned long display_unit) FAST_FUNC;
1114/* Put a string of hex bytes ("1b2e66fe"...), return advanced pointer */ 1125/* Put a string of hex bytes ("1b2e66fe"...), return advanced pointer */
diff --git a/libbb/copy_file.c b/libbb/copy_file.c
index 9b10dda1f..c0928a5a8 100644
--- a/libbb/copy_file.c
+++ b/libbb/copy_file.c
@@ -117,6 +117,8 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
117 return -1; 117 return -1;
118 } 118 }
119#endif 119#endif
120 if (flags & FILEUTILS_NO_OVERWRITE) /* cp -n */
121 return 0;
120 dest_exists = 1; 122 dest_exists = 1;
121 } 123 }
122 124
diff --git a/libbb/dump.c b/libbb/dump.c
index 196ccfe65..ffae04786 100644
--- a/libbb/dump.c
+++ b/libbb/dump.c
@@ -34,7 +34,6 @@ typedef struct priv_dumper_t {
34 FU *endfu; 34 FU *endfu;
35 off_t savaddress; /* saved address/offset in stream */ 35 off_t savaddress; /* saved address/offset in stream */
36 off_t eaddress; /* end address */ 36 off_t eaddress; /* end address */
37 off_t address; /* address/offset in stream */
38 int blocksize; 37 int blocksize;
39 smallint exitval; /* final exit value */ 38 smallint exitval; /* final exit value */
40#if ENABLE_PLATFORM_MINGW32 39#if ENABLE_PLATFORM_MINGW32
@@ -217,7 +216,7 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
217 pr->bcnt = fu->bcnt; 216 pr->bcnt = fu->bcnt;
218 if (fu->bcnt == 0) { 217 if (fu->bcnt == 0) {
219 if (!prec) 218 if (!prec)
220 bb_simple_error_msg_and_die("%%s needs precision or byte count"); 219 bb_simple_error_msg_and_die("%s needs precision or byte count");
221 pr->bcnt = atoi(prec); 220 pr->bcnt = atoi(prec);
222 } 221 }
223 } else 222 } else
@@ -234,7 +233,8 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
234 if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) { 233 if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) {
235 goto DO_BAD_CONV_CHAR; 234 goto DO_BAD_CONV_CHAR;
236 } 235 }
237 *p1 = p1[2]; 236 *p1++ = 'l';
237 *p1++ = 'l';
238 break; 238 break;
239 case 'c': /* %_c: chars, \ooo, \n \r \t etc */ 239 case 'c': /* %_c: chars, \ooo, \n \r \t etc */
240 pr->flags = F_C; 240 pr->flags = F_C;
@@ -344,7 +344,7 @@ static void do_skip(priv_dumper_t *dumper, const char *fname)
344 ) { 344 ) {
345 /* If st_size is valid and pub.dump_skip >= st_size */ 345 /* If st_size is valid and pub.dump_skip >= st_size */
346 dumper->pub.dump_skip -= sbuf.st_size; 346 dumper->pub.dump_skip -= sbuf.st_size;
347 dumper->address += sbuf.st_size; 347 dumper->pub.address += sbuf.st_size;
348 return; 348 return;
349 } 349 }
350#if ENABLE_PLATFORM_MINGW32 350#if ENABLE_PLATFORM_MINGW32
@@ -354,8 +354,8 @@ static void do_skip(priv_dumper_t *dumper, const char *fname)
354#endif 354#endif
355 bb_simple_perror_msg_and_die(fname); 355 bb_simple_perror_msg_and_die(fname);
356 } 356 }
357 dumper->address += dumper->pub.dump_skip; 357 dumper->pub.address += dumper->pub.dump_skip;
358 dumper->savaddress = dumper->address; 358 dumper->savaddress = dumper->pub.address;
359 dumper->pub.dump_skip = 0; 359 dumper->pub.dump_skip = 0;
360} 360}
361 361
@@ -375,6 +375,7 @@ static NOINLINE int next(priv_dumper_t *dumper)
375#endif 375#endif
376 bb_simple_perror_msg(fname); 376 bb_simple_perror_msg(fname);
377 dumper->exitval = 1; 377 dumper->exitval = 1;
378 dumper->next__done = 1;
378 continue; 379 continue;
379 } 380 }
380 } 381 }
@@ -403,7 +404,7 @@ static unsigned char *get(priv_dumper_t *dumper)
403 int blocksize = dumper->blocksize; 404 int blocksize = dumper->blocksize;
404 405
405 if (!dumper->get__curp) { 406 if (!dumper->get__curp) {
406 dumper->address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/ 407 dumper->pub.address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/
407 dumper->get__curp = xmalloc(blocksize); 408 dumper->get__curp = xmalloc(blocksize);
408 dumper->get__savp = xzalloc(blocksize); /* need to be initialized */ 409 dumper->get__savp = xzalloc(blocksize); /* need to be initialized */
409 } else { 410 } else {
@@ -411,7 +412,7 @@ static unsigned char *get(priv_dumper_t *dumper)
411 dumper->get__curp = dumper->get__savp; 412 dumper->get__curp = dumper->get__savp;
412 dumper->get__savp = tmp; 413 dumper->get__savp = tmp;
413 dumper->savaddress += blocksize; 414 dumper->savaddress += blocksize;
414 dumper->address = dumper->savaddress; 415 dumper->pub.address = dumper->savaddress;
415 } 416 }
416 need = blocksize; 417 need = blocksize;
417 nread = 0; 418 nread = 0;
@@ -434,7 +435,7 @@ static unsigned char *get(priv_dumper_t *dumper)
434 } 435 }
435 } 436 }
436 memset(dumper->get__curp + nread, 0, need); 437 memset(dumper->get__curp + nread, 0, need);
437 dumper->eaddress = dumper->address + nread; 438 dumper->eaddress = dumper->pub.address + nread;
438 return dumper->get__curp; 439 return dumper->get__curp;
439 } 440 }
440#if ENABLE_PLATFORM_MINGW32 441#if ENABLE_PLATFORM_MINGW32
@@ -478,7 +479,7 @@ static unsigned char *get(priv_dumper_t *dumper)
478 } 479 }
479 dumper->pub.dump_vflag = DUP; 480 dumper->pub.dump_vflag = DUP;
480 dumper->savaddress += blocksize; 481 dumper->savaddress += blocksize;
481 dumper->address = dumper->savaddress; 482 dumper->pub.address = dumper->savaddress;
482 need = blocksize; 483 need = blocksize;
483 nread = 0; 484 nread = 0;
484 } else { 485 } else {
@@ -579,8 +580,8 @@ static void display(priv_dumper_t* dumper)
579 580
580 fs = dumper->pub.fshead; 581 fs = dumper->pub.fshead;
581 savebp = bp; 582 savebp = bp;
582 saveaddress = dumper->address; 583 saveaddress = dumper->pub.address;
583 for (; fs; fs = fs->nextfs, bp = savebp, dumper->address = saveaddress) { 584 for (; fs; fs = fs->nextfs, bp = savebp, dumper->pub.address = saveaddress) {
584 FU *fu; 585 FU *fu;
585 for (fu = fs->nextfu; fu; fu = fu->nextfu) { 586 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
586 int cnt; 587 int cnt;
@@ -589,14 +590,14 @@ static void display(priv_dumper_t* dumper)
589 } 590 }
590 for (cnt = fu->reps; cnt; --cnt) { 591 for (cnt = fu->reps; cnt; --cnt) {
591 PR *pr; 592 PR *pr;
592 for (pr = fu->nextpr; pr; dumper->address += pr->bcnt, 593 for (pr = fu->nextpr; pr; dumper->pub.address += pr->bcnt,
593 bp += pr->bcnt, pr = pr->nextpr) { 594 bp += pr->bcnt, pr = pr->nextpr) {
594 if (dumper->eaddress 595 if (dumper->eaddress
595 && dumper->address >= dumper->eaddress 596 && dumper->pub.address >= dumper->eaddress
596 ) { 597 ) {
597 if (dumper->pub.eofstring) { 598 if (dumper->pub.xxd_eofstring) {
598 /* xxd support: requested to not pad incomplete blocks */ 599 /* xxd support: requested to not pad incomplete blocks */
599 fputs_stdout(dumper->pub.eofstring); 600 fputs_stdout(dumper->pub.xxd_eofstring);
600 return; 601 return;
601 } 602 }
602 if (!(pr->flags & (F_TEXT | F_BPAD))) 603 if (!(pr->flags & (F_TEXT | F_BPAD)))
@@ -608,7 +609,7 @@ static void display(priv_dumper_t* dumper)
608 } 609 }
609 switch (pr->flags) { 610 switch (pr->flags) {
610 case F_ADDRESS: 611 case F_ADDRESS:
611 printf(pr->fmt, (unsigned) dumper->address); 612 printf(pr->fmt, (unsigned long long) dumper->pub.address + dumper->pub.xxd_displayoff);
612 break; 613 break;
613 case F_BPAD: 614 case F_BPAD:
614 printf(pr->fmt, ""); 615 printf(pr->fmt, "");
@@ -702,15 +703,15 @@ static void display(priv_dumper_t* dumper)
702 * of blocksize, and no partial block ever found. 703 * of blocksize, and no partial block ever found.
703 */ 704 */
704 if (!dumper->eaddress) { 705 if (!dumper->eaddress) {
705 if (!dumper->address) { 706 if (!dumper->pub.address) {
706 return; 707 return;
707 } 708 }
708 dumper->eaddress = dumper->address; 709 dumper->eaddress = dumper->pub.address;
709 } 710 }
710 for (pr = dumper->endfu->nextpr; pr; pr = pr->nextpr) { 711 for (pr = dumper->endfu->nextpr; pr; pr = pr->nextpr) {
711 switch (pr->flags) { 712 switch (pr->flags) {
712 case F_ADDRESS: 713 case F_ADDRESS:
713 printf(pr->fmt, (unsigned) dumper->eaddress); 714 printf(pr->fmt, (unsigned long long) dumper->eaddress + dumper->pub.xxd_displayoff);
714 break; 715 break;
715 case F_TEXT: 716 case F_TEXT:
716 printf(pr->fmt); 717 printf(pr->fmt);
diff --git a/libbb/iterate_on_dir.c b/libbb/iterate_on_dir.c
new file mode 100644
index 000000000..deef72ebf
--- /dev/null
+++ b/libbb/iterate_on_dir.c
@@ -0,0 +1,28 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * See README for additional information
4 *
5 * Licensed under GPLv2, see file LICENSE in this source tree.
6 */
7//kbuild:lib-y += iterate_on_dir.o
8
9#include "libbb.h"
10
11/* Iterate a function on each entry of a directory */
12int FAST_FUNC iterate_on_dir(const char *dir_name,
13 int FAST_FUNC (*func)(const char *, struct dirent *, void *),
14 void *private)
15{
16 DIR *dir;
17 struct dirent *de;
18
19 dir = opendir(dir_name);
20 if (dir == NULL) {
21 return -1;
22 }
23 while ((de = readdir(dir)) != NULL) {
24 func(dir_name, de, private);
25 }
26 closedir(dir);
27 return 0;
28}
diff --git a/libbb/remove_file.c b/libbb/remove_file.c
index cea5d47e6..1505e6218 100644
--- a/libbb/remove_file.c
+++ b/libbb/remove_file.c
@@ -60,11 +60,7 @@ int FAST_FUNC remove_file(const char *path, int flags)
60 status = -1; 60 status = -1;
61 free(new_path); 61 free(new_path);
62 } 62 }
63 63 closedir(dp);
64 if (closedir(dp) < 0) {
65 bb_perror_msg("can't close '%s'", path);
66 return -1;
67 }
68 64
69 if (flags & FILEUTILS_INTERACTIVE) { 65 if (flags & FILEUTILS_INTERACTIVE) {
70 fprintf(stderr, "%s: remove directory '%s'? ", 66 fprintf(stderr, "%s: remove directory '%s'? ",
diff --git a/loginutils/su.c b/loginutils/su.c
index e46bbf78b..647c97fb1 100644
--- a/loginutils/su.c
+++ b/loginutils/su.c
@@ -36,7 +36,7 @@
36//kbuild:lib-$(CONFIG_SU) += su.o 36//kbuild:lib-$(CONFIG_SU) += su.o
37 37
38//usage:#define su_trivial_usage 38//usage:#define su_trivial_usage
39//usage: "[-lmp] [-] [-s SH] [USER [SCRIPT ARGS / -c 'CMD' ARG0 ARGS]]" 39//usage: "[-lmp] [-s SH] [-] [USER [FILE ARGS | -c 'CMD' [ARG0 ARGS]]]"
40//usage:#define su_full_usage "\n\n" 40//usage:#define su_full_usage "\n\n"
41//usage: "Run shell under USER (by default, root)\n" 41//usage: "Run shell under USER (by default, root)\n"
42//usage: "\n -,-l Clear environment, go to home dir, run shell as login shell" 42//usage: "\n -,-l Clear environment, go to home dir, run shell as login shell"
diff --git a/miscutils/ascii.c b/miscutils/ascii.c
new file mode 100644
index 000000000..98c11fa51
--- /dev/null
+++ b/miscutils/ascii.c
@@ -0,0 +1,51 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright (C) 2021 Denys Vlasenko <vda.linux@googlemail.com>
4 *
5 * Licensed under GPLv2, see file LICENSE in this source tree.
6 */
7//config:config ASCII
8//config: bool "ascii"
9//config: default y
10//config: help
11//config: Print ascii table.
12//config:
13
14//applet:IF_ASCII(APPLET(ascii, BB_DIR_USR_BIN, BB_SUID_DROP))
15
16//kbuild:lib-$(CONFIG_ASCII) += ascii.o
17
18//usage:#define ascii_trivial_usage NOUSAGE_STR
19//usage:#define ascii_full_usage ""
20
21#include "libbb.h"
22
23int ascii_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
24int ascii_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
25{
26 const char *ctrl =
27 "NUL""SOH""STX""ETX""EOT""ENQ""ACK""BEL"
28 "BS ""HT ""NL ""VT ""FF ""CR ""SO ""SI "
29 "DLE""DC1""DC2""DC3""DC4""NAK""SYN""ETB"
30 "CAN""EM ""SUB""ESC""FS ""GS ""RS ""US "
31 ;
32//TODO: od has a similar table, can we reuse it?
33 char last[2];
34 unsigned i;
35
36 last[1] = '\0';
37 printf("Dec Hex Dec Hex Dec Hex Dec Hex Dec Hex Dec Hex Dec Hex Dec Hex\n");
38 for (i = 0; i < 16; i++) {
39 printf("%3u %02x %.3s%4u %02x %.3s%4u %02x %c%4u %02x %c%4u %02x %c%4u %02x %c%5u %02x %c%5u %02x %s\n",
40 i+0x00, i+0x00, ctrl + i*3,
41 i+0x10, i+0x10, ctrl + i*3 + 16*3,
42 i+0x20, i+0x20, i+0x20,
43 i+0x30, i+0x30, i+0x30,
44 i+0x40, i+0x40, i+0x40,
45 i+0x50, i+0x50, i+0x50,
46 i+0x60, i+0x60, i+0x60,
47 i+0x70, i+0x70, (i+0x70 == 0x7f ? "DEL" : (last[0] = i+0x70, last))
48 );
49 }
50 return EXIT_SUCCESS;
51}
diff --git a/miscutils/bc.c b/miscutils/bc.c
index d74ab1da2..f931be5cc 100644
--- a/miscutils/bc.c
+++ b/miscutils/bc.c
@@ -5,7 +5,7 @@
5 * Original code copyright (c) 2018 Gavin D. Howard and contributors. 5 * Original code copyright (c) 2018 Gavin D. Howard and contributors.
6 */ 6 */
7//TODO: 7//TODO:
8// maybe implement a^b for non-integer b? 8// maybe implement a^b for non-integer b? (see zbc_num_p())
9 9
10#define DEBUG_LEXER 0 10#define DEBUG_LEXER 0
11#define DEBUG_COMPILE 0 11#define DEBUG_COMPILE 0
@@ -108,7 +108,7 @@
108 108
109//See www.gnu.org/software/bc/manual/bc.html 109//See www.gnu.org/software/bc/manual/bc.html
110//usage:#define bc_trivial_usage 110//usage:#define bc_trivial_usage
111//usage: "[-sqlw] [FILE...]" 111//usage: "[-sqlw] [FILE]..."
112//usage: 112//usage:
113//usage:#define bc_full_usage "\n" 113//usage:#define bc_full_usage "\n"
114//usage: "\nArbitrary precision calculator" 114//usage: "\nArbitrary precision calculator"
@@ -1386,6 +1386,12 @@ static void bc_num_copy(BcNum *d, BcNum *s)
1386 } 1386 }
1387} 1387}
1388 1388
1389static void bc_num_init_and_copy(BcNum *d, BcNum *s)
1390{
1391 bc_num_init(d, s->len);
1392 bc_num_copy(d, s);
1393}
1394
1389static BC_STATUS zbc_num_ulong_abs(BcNum *n, unsigned long *result_p) 1395static BC_STATUS zbc_num_ulong_abs(BcNum *n, unsigned long *result_p)
1390{ 1396{
1391 size_t i; 1397 size_t i;
@@ -1985,11 +1991,8 @@ static FAST_FUNC BC_STATUS zbc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size
1985 scale = BC_MIN(a->rdx + b->rdx, scale); 1991 scale = BC_MIN(a->rdx + b->rdx, scale);
1986 maxrdx = BC_MAX(maxrdx, scale); 1992 maxrdx = BC_MAX(maxrdx, scale);
1987 1993
1988 bc_num_init(&cpa, a->len); 1994 bc_num_init_and_copy(&cpa, a);
1989 bc_num_init(&cpb, b->len); 1995 bc_num_init_and_copy(&cpb, b);
1990
1991 bc_num_copy(&cpa, a);
1992 bc_num_copy(&cpb, b);
1993 cpa.neg = cpb.neg = false; 1996 cpa.neg = cpb.neg = false;
1994 1997
1995 s = zbc_num_shift(&cpa, maxrdx); 1998 s = zbc_num_shift(&cpa, maxrdx);
@@ -2152,6 +2155,7 @@ static FAST_FUNC BC_STATUS zbc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size
2152 BcNum copy; 2155 BcNum copy;
2153 unsigned long pow; 2156 unsigned long pow;
2154 size_t i, powrdx, resrdx; 2157 size_t i, powrdx, resrdx;
2158 size_t a_rdx;
2155 bool neg; 2159 bool neg;
2156 2160
2157 // GNU bc does not allow 2^2.0 - we do 2161 // GNU bc does not allow 2^2.0 - we do
@@ -2159,6 +2163,9 @@ static FAST_FUNC BC_STATUS zbc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size
2159 if (b->num[i] != 0) 2163 if (b->num[i] != 0)
2160 RETURN_STATUS(bc_error("not an integer")); 2164 RETURN_STATUS(bc_error("not an integer"));
2161 2165
2166// a^b for non-integer b (for a>0) can be implemented as exp(ln(a)*b).
2167// Possibly better precision would be given by a^int(b) * exp(ln(a)*frac(b)).
2168
2162 if (b->len == 0) { 2169 if (b->len == 0) {
2163 bc_num_one(c); 2170 bc_num_one(c);
2164 RETURN_STATUS(BC_STATUS_SUCCESS); 2171 RETURN_STATUS(BC_STATUS_SUCCESS);
@@ -2180,18 +2187,31 @@ static FAST_FUNC BC_STATUS zbc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size
2180 if (s) RETURN_STATUS(s); 2187 if (s) RETURN_STATUS(s);
2181 // b is not used beyond this point 2188 // b is not used beyond this point
2182 2189
2183 bc_num_init(&copy, a->len); 2190 bc_num_init_and_copy(&copy, a);
2184 bc_num_copy(&copy, a); 2191 a_rdx = a->rdx; // pull it into a CPU register (hopefully)
2192 // a is not used beyond this point
2185 2193
2186 if (!neg) { 2194 if (!neg) {
2187 if (a->rdx > scale) 2195 unsigned long new_scale;
2188 scale = a->rdx; 2196 if (a_rdx > scale)
2189 if (a->rdx * pow < scale) 2197 scale = a_rdx;
2190 scale = a->rdx * pow; 2198 new_scale = a_rdx * pow;
2191 } 2199 // Don't fall for multiplication overflow. Example:
2192 2200 // 0.01^2147483648 a_rdx:2 pow:0x80000000, 32bit mul is 0.
2193 2201//not that it matters with current algorithm, it would OOM on such large powers,
2194 for (powrdx = a->rdx; !(pow & 1); pow >>= 1) { 2202//but it can be improved to detect zero results etc. Example: with scale=0,
2203//result of 0.01^N for any N>1 is 0: 0.01^2 = 0.0001 ~= 0.00 (trunc to scale)
2204//then this would matter:
2205 // if a_rdx != 0 and new_scale < pow, we had overflow,
2206 // correct "new_scale" value is larger than ULONG_MAX,
2207 // thus larger than any possible current value of "scale",
2208 // thus "scale = new_scale" should not be done:
2209 if (a_rdx == 0 || new_scale >= pow)
2210 if (new_scale < scale)
2211 scale = new_scale;
2212 }
2213
2214 for (powrdx = a_rdx; !(pow & 1); pow >>= 1) {
2195 powrdx <<= 1; 2215 powrdx <<= 1;
2196 s = zbc_num_mul(&copy, &copy, &copy, powrdx); 2216 s = zbc_num_mul(&copy, &copy, &copy, powrdx);
2197 if (s) goto err; 2217 if (s) goto err;
@@ -2493,8 +2513,7 @@ static void bc_array_copy(BcVec *d, const BcVec *s)
2493 dnum = (void*)d->v; 2513 dnum = (void*)d->v;
2494 snum = (void*)s->v; 2514 snum = (void*)s->v;
2495 for (i = 0; i < s->len; i++, dnum++, snum++) { 2515 for (i = 0; i < s->len; i++, dnum++, snum++) {
2496 bc_num_init(dnum, snum->len); 2516 bc_num_init_and_copy(dnum, snum);
2497 bc_num_copy(dnum, snum);
2498 } 2517 }
2499} 2518}
2500 2519
@@ -2508,8 +2527,7 @@ static void dc_result_copy(BcResult *d, BcResult *src)
2508 case XC_RESULT_IBASE: 2527 case XC_RESULT_IBASE:
2509 case XC_RESULT_SCALE: 2528 case XC_RESULT_SCALE:
2510 case XC_RESULT_OBASE: 2529 case XC_RESULT_OBASE:
2511 bc_num_init(&d->d.n, src->d.n.len); 2530 bc_num_init_and_copy(&d->d.n, &src->d.n);
2512 bc_num_copy(&d->d.n, &src->d.n);
2513 break; 2531 break;
2514 case XC_RESULT_VAR: 2532 case XC_RESULT_VAR:
2515 case XC_RESULT_ARRAY: 2533 case XC_RESULT_ARRAY:
@@ -5611,11 +5629,10 @@ static BC_STATUS zxc_num_printNum(BcNum *n, unsigned base_t, size_t width, BcNum
5611 } 5629 }
5612 5630
5613 bc_vec_init(&stack, sizeof(long), NULL); 5631 bc_vec_init(&stack, sizeof(long), NULL);
5614 bc_num_init(&intp, n->len); 5632 bc_num_init_and_copy(&intp, n);
5615 bc_num_init(&fracp, n->rdx); 5633 bc_num_init(&fracp, n->rdx);
5616 bc_num_init(&digit, width); 5634 bc_num_init(&digit, width);
5617 bc_num_init(&frac_len, BC_NUM_INT(n)); 5635 bc_num_init(&frac_len, BC_NUM_INT(n));
5618 bc_num_copy(&intp, n);
5619 bc_num_one(&frac_len); 5636 bc_num_one(&frac_len);
5620 base.cap = ARRAY_SIZE(base_digs); 5637 base.cap = ARRAY_SIZE(base_digs);
5621 base.num = base_digs; 5638 base.num = base_digs;
@@ -5784,8 +5801,7 @@ static BC_STATUS zxc_program_negate(void)
5784 s = zxc_program_prep(&ptr, &num); 5801 s = zxc_program_prep(&ptr, &num);
5785 if (s) RETURN_STATUS(s); 5802 if (s) RETURN_STATUS(s);
5786 5803
5787 bc_num_init(&res.d.n, num->len); 5804 bc_num_init_and_copy(&res.d.n, num);
5788 bc_num_copy(&res.d.n, num);
5789 if (res.d.n.len) res.d.n.neg = !res.d.n.neg; 5805 if (res.d.n.len) res.d.n.neg = !res.d.n.neg;
5790 5806
5791 xc_program_retire(&res, XC_RESULT_TEMP); 5807 xc_program_retire(&res, XC_RESULT_TEMP);
@@ -6024,8 +6040,7 @@ static BC_STATUS zxc_program_assign(char inst)
6024 s = BC_STATUS_SUCCESS; 6040 s = BC_STATUS_SUCCESS;
6025 } 6041 }
6026 6042
6027 bc_num_init(&res.d.n, l->len); 6043 bc_num_init_and_copy(&res.d.n, l);
6028 bc_num_copy(&res.d.n, l);
6029 xc_program_binOpRetire(&res); 6044 xc_program_binOpRetire(&res);
6030 6045
6031 RETURN_STATUS(s); 6046 RETURN_STATUS(s);
@@ -6122,8 +6137,7 @@ static BC_STATUS zbc_program_incdec(char inst)
6122 6137
6123 if (inst == BC_INST_INC_POST || inst == BC_INST_DEC_POST) { 6138 if (inst == BC_INST_INC_POST || inst == BC_INST_DEC_POST) {
6124 copy.t = XC_RESULT_TEMP; 6139 copy.t = XC_RESULT_TEMP;
6125 bc_num_init(&copy.d.n, num->len); 6140 bc_num_init_and_copy(&copy.d.n, num);
6126 bc_num_copy(&copy.d.n, num);
6127 } 6141 }
6128 6142
6129 res.t = BC_RESULT_ONE; 6143 res.t = BC_RESULT_ONE;
@@ -6225,8 +6239,7 @@ static BC_STATUS zbc_program_return(char inst)
6225 6239
6226 s = zxc_program_num(operand, &num); 6240 s = zxc_program_num(operand, &num);
6227 if (s) RETURN_STATUS(s); 6241 if (s) RETURN_STATUS(s);
6228 bc_num_init(&res.d.n, num->len); 6242 bc_num_init_and_copy(&res.d.n, num);
6229 bc_num_copy(&res.d.n, num);
6230 bc_vec_pop(&G.prog.results); 6243 bc_vec_pop(&G.prog.results);
6231 } else { 6244 } else {
6232 if (f->voidfunc) 6245 if (f->voidfunc)
diff --git a/miscutils/chat.c b/miscutils/chat.c
index 86a114df6..f9e12a4ac 100644
--- a/miscutils/chat.c
+++ b/miscutils/chat.c
@@ -79,7 +79,7 @@
79//kbuild:lib-$(CONFIG_CHAT) += chat.o 79//kbuild:lib-$(CONFIG_CHAT) += chat.o
80 80
81//usage:#define chat_trivial_usage 81//usage:#define chat_trivial_usage
82//usage: "EXPECT [SEND [EXPECT [SEND...]]]" 82//usage: "EXPECT [SEND [EXPECT [SEND]]...]"
83//usage:#define chat_full_usage "\n\n" 83//usage:#define chat_full_usage "\n\n"
84//usage: "Useful for interacting with a modem connected to stdin/stdout.\n" 84//usage: "Useful for interacting with a modem connected to stdin/stdout.\n"
85//usage: "A script consists of \"expect-send\" argument pairs.\n" 85//usage: "A script consists of \"expect-send\" argument pairs.\n"
diff --git a/miscutils/flashcp.c b/miscutils/flashcp.c
index 1ca9d158d..93c80cc6c 100644
--- a/miscutils/flashcp.c
+++ b/miscutils/flashcp.c
@@ -19,9 +19,9 @@
19//kbuild:lib-$(CONFIG_FLASHCP) += flashcp.o 19//kbuild:lib-$(CONFIG_FLASHCP) += flashcp.o
20 20
21//usage:#define flashcp_trivial_usage 21//usage:#define flashcp_trivial_usage
22//usage: "-v FILE MTD_DEVICE" 22//usage: "[-v] FILE MTD_DEVICE"
23//usage:#define flashcp_full_usage "\n\n" 23//usage:#define flashcp_full_usage "\n\n"
24//usage: "Copy an image to MTD device\n" 24//usage: "Copy FILE to MTD device\n"
25//usage: "\n -v Verbose" 25//usage: "\n -v Verbose"
26 26
27#include "libbb.h" 27#include "libbb.h"
diff --git a/miscutils/i2c_tools.c b/miscutils/i2c_tools.c
index 48135921d..b25d49792 100644
--- a/miscutils/i2c_tools.c
+++ b/miscutils/i2c_tools.c
@@ -1394,7 +1394,7 @@ static void check_i2c_func(int fd)
1394} 1394}
1395 1395
1396//usage:#define i2ctransfer_trivial_usage 1396//usage:#define i2ctransfer_trivial_usage
1397//usage: "[-fay] I2CBUS {rLENGTH[@ADDR] | wLENGTH[@ADDR] DATA...}..." 1397//usage: "[-fay] I2CBUS { rLENGTH[@ADDR] | wLENGTH[@ADDR] DATA...}..."
1398//usage:#define i2ctransfer_full_usage "\n\n" 1398//usage:#define i2ctransfer_full_usage "\n\n"
1399//usage: "Read/write I2C data in one transfer" 1399//usage: "Read/write I2C data in one transfer"
1400//usage: "\n" 1400//usage: "\n"
diff --git a/miscutils/man.c b/miscutils/man.c
index 052b50054..be3b2a000 100644
--- a/miscutils/man.c
+++ b/miscutils/man.c
@@ -350,7 +350,7 @@ int man_main(int argc UNUSED_PARAM, char **argv)
350 350
351 /* is 1st ARG a SECTION? */ 351 /* is 1st ARG a SECTION? */
352 sec_list = conf_sec_list; 352 sec_list = conf_sec_list;
353 if (is_section_name(conf_sec_list, *argv)) { 353 if (is_section_name(conf_sec_list, *argv) && argv[1]) {
354 /* yes */ 354 /* yes */
355 sec_list = *argv++; 355 sec_list = *argv++;
356 } 356 }
diff --git a/miscutils/microcom.c b/miscutils/microcom.c
index 399d4cf7f..97b46342f 100644
--- a/miscutils/microcom.c
+++ b/miscutils/microcom.c
@@ -18,14 +18,14 @@
18//kbuild:lib-$(CONFIG_MICROCOM) += microcom.o 18//kbuild:lib-$(CONFIG_MICROCOM) += microcom.o
19 19
20//usage:#define microcom_trivial_usage 20//usage:#define microcom_trivial_usage
21//usage: "[-d DELAY] [-t TIMEOUT] [-s SPEED] [-X] TTY" 21//usage: "[-d DELAY_MS] [-t TIMEOUT_MS ] [-s SPEED] [-X] TTY"
22//usage:#define microcom_full_usage "\n\n" 22//usage:#define microcom_full_usage "\n\n"
23//usage: "Copy bytes for stdin to TTY and from TTY to stdout\n" 23//usage: "Copy bytes from stdin to TTY and from TTY to stdout\n"
24//usage: "\n -d Wait up to DELAY ms for TTY output before sending every" 24//usage: "\n -d DELAY Wait up to DELAY ms for TTY output before sending"
25//usage: "\n next byte to it" 25//usage: "\n every next byte to it"
26//usage: "\n -t Exit if both stdin and TTY are silent for TIMEOUT ms" 26//usage: "\n -t TIMEOUT Exit if both stdin and TTY are silent for TIMEOUT ms"
27//usage: "\n -s Set serial line to SPEED" 27//usage: "\n -s SPEED Set serial line to SPEED"
28//usage: "\n -X Disable special meaning of NUL and Ctrl-X from stdin" 28//usage: "\n -X Disable special meaning of NUL and Ctrl-X from stdin"
29 29
30#include "libbb.h" 30#include "libbb.h"
31#include "common_bufsiz.h" 31#include "common_bufsiz.h"
diff --git a/miscutils/mt.c b/miscutils/mt.c
index 1a4214664..52d5476a1 100644
--- a/miscutils/mt.c
+++ b/miscutils/mt.c
@@ -19,7 +19,7 @@
19//usage:#define mt_full_usage "\n\n" 19//usage:#define mt_full_usage "\n\n"
20//usage: "Control magnetic tape drive operation\n" 20//usage: "Control magnetic tape drive operation\n"
21//usage: "\n" 21//usage: "\n"
22//usage: "Available Opcodes:\n" 22//usage: "Opcodes:\n"
23//usage: "\n" 23//usage: "\n"
24//usage: "bsf bsfm bsr bss datacompression drvbuffer eof eom erase\n" 24//usage: "bsf bsfm bsr bss datacompression drvbuffer eof eom erase\n"
25//usage: "fsf fsfm fsr fss load lock mkpart nop offline ras1 ras2\n" 25//usage: "fsf fsfm fsr fss load lock mkpart nop offline ras1 ras2\n"
diff --git a/miscutils/strings.c b/miscutils/strings.c
index e4a68227e..b01884968 100644
--- a/miscutils/strings.c
+++ b/miscutils/strings.c
@@ -18,7 +18,7 @@
18//kbuild:lib-$(CONFIG_STRINGS) += strings.o 18//kbuild:lib-$(CONFIG_STRINGS) += strings.o
19 19
20//usage:#define strings_trivial_usage 20//usage:#define strings_trivial_usage
21//usage: "[-fo] [-t o/d/x] [-n LEN] [FILE]..." 21//usage: "[-fo] [-t o|d|x] [-n LEN] [FILE]..."
22//usage:#define strings_full_usage "\n\n" 22//usage:#define strings_full_usage "\n\n"
23//usage: "Display printable strings in a binary file\n" 23//usage: "Display printable strings in a binary file\n"
24//We usually don't bother user with "nop" options. They work, but are not shown: 24//We usually don't bother user with "nop" options. They work, but are not shown:
@@ -26,7 +26,7 @@
26//unimplemented alternative is -d: Only strings from initialized, loaded data sections 26//unimplemented alternative is -d: Only strings from initialized, loaded data sections
27//usage: "\n -f Precede strings with filenames" 27//usage: "\n -f Precede strings with filenames"
28//usage: "\n -o Precede strings with octal offsets" 28//usage: "\n -o Precede strings with octal offsets"
29//usage: "\n -t o/d/x Precede strings with offsets in base 8/10/16" 29//usage: "\n -t o|d|x Precede strings with offsets in base 8/10/16"
30//usage: "\n -n LEN At least LEN characters form a string (default 4)" 30//usage: "\n -n LEN At least LEN characters form a string (default 4)"
31 31
32#include "libbb.h" 32#include "libbb.h"
diff --git a/miscutils/ts.c b/miscutils/ts.c
index 3eecb7464..af6677fc7 100644
--- a/miscutils/ts.c
+++ b/miscutils/ts.c
@@ -13,7 +13,10 @@
13 13
14//usage:#define ts_trivial_usage 14//usage:#define ts_trivial_usage
15//usage: "[-is] [STRFTIME]" 15//usage: "[-is] [STRFTIME]"
16//usage:#define ts_full_usage "" 16//usage:#define ts_full_usage "\n\n"
17//usage: "Pipe stdin to stdout, add timestamp to each line\n"
18//usage: "\n -s Time since start"
19//usage: "\n -i Time since previous line"
17 20
18#include "libbb.h" 21#include "libbb.h"
19#include "common_bufsiz.h" 22#include "common_bufsiz.h"
diff --git a/miscutils/ubi_tools.c b/miscutils/ubi_tools.c
index 69ead7a13..6d49f61d9 100644
--- a/miscutils/ubi_tools.c
+++ b/miscutils/ubi_tools.c
@@ -251,7 +251,7 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
251 } else 251 } else
252 252
253//usage:#define ubirmvol_trivial_usage 253//usage:#define ubirmvol_trivial_usage
254//usage: "-n VOLID / -N VOLNAME UBI_DEVICE" 254//usage: "-n VOLID | -N VOLNAME UBI_DEVICE"
255//usage:#define ubirmvol_full_usage "\n\n" 255//usage:#define ubirmvol_full_usage "\n\n"
256//usage: "Remove UBI volume\n" 256//usage: "Remove UBI volume\n"
257//usage: "\n -n VOLID Volume ID" 257//usage: "\n -n VOLID Volume ID"
diff --git a/modutils/modinfo.c b/modutils/modinfo.c
index d15772f0d..0a86c3296 100644
--- a/modutils/modinfo.c
+++ b/modutils/modinfo.c
@@ -131,7 +131,7 @@ static void modinfo(const char *path, const char *version,
131//usage: "\n -p Shortcut for '-F parm'" 131//usage: "\n -p Shortcut for '-F parm'"
132////usage: "\n -n Shortcut for '-F filename'" 132////usage: "\n -n Shortcut for '-F filename'"
133//usage: "\n -F keyword Keyword to look for" 133//usage: "\n -F keyword Keyword to look for"
134//usage: "\n -0 Separate output with NULs" 134//usage: "\n -0 NUL terminated output"
135//usage:#define modinfo_example_usage 135//usage:#define modinfo_example_usage
136//usage: "$ modinfo -F vermagic loop\n" 136//usage: "$ modinfo -F vermagic loop\n"
137 137
diff --git a/networking/ftpd.c b/networking/ftpd.c
index 6ca231c90..0d6a289c7 100644
--- a/networking/ftpd.c
+++ b/networking/ftpd.c
@@ -54,7 +54,7 @@
54//kbuild:lib-$(CONFIG_FTPD) += ftpd.o 54//kbuild:lib-$(CONFIG_FTPD) += ftpd.o
55 55
56//usage:#define ftpd_trivial_usage 56//usage:#define ftpd_trivial_usage
57//usage: "[-wvS]"IF_FEATURE_FTPD_AUTHENTICATION(" [-a USER]")" [-t N] [-T N] [DIR]" 57//usage: "[-wvS]"IF_FEATURE_FTPD_AUTHENTICATION(" [-a USER]")" [-t SEC] [-T SEC] [DIR]"
58//usage:#define ftpd_full_usage "\n\n" 58//usage:#define ftpd_full_usage "\n\n"
59//usage: IF_NOT_FEATURE_FTPD_AUTHENTICATION( 59//usage: IF_NOT_FEATURE_FTPD_AUTHENTICATION(
60//usage: "Anonymous FTP server. Client access occurs under ftpd's UID.\n" 60//usage: "Anonymous FTP server. Client access occurs under ftpd's UID.\n"
@@ -63,7 +63,7 @@
63//usage: "FTP server. " 63//usage: "FTP server. "
64//usage: ) 64//usage: )
65//usage: "Chroots to DIR, if this fails (run by non-root), cds to it.\n" 65//usage: "Chroots to DIR, if this fails (run by non-root), cds to it.\n"
66//usage: "Should be used as inetd service, inetd.conf line:\n" 66//usage: "It is an inetd service, inetd.conf line:\n"
67//usage: " 21 stream tcp nowait root ftpd ftpd /files/to/serve\n" 67//usage: " 21 stream tcp nowait root ftpd ftpd /files/to/serve\n"
68//usage: "Can be run from tcpsvd:\n" 68//usage: "Can be run from tcpsvd:\n"
69//usage: " tcpsvd -vE 0.0.0.0 21 ftpd /files/to/serve" 69//usage: " tcpsvd -vE 0.0.0.0 21 ftpd /files/to/serve"
diff --git a/networking/nameif.c b/networking/nameif.c
index 854594c83..66e042688 100644
--- a/networking/nameif.c
+++ b/networking/nameif.c
@@ -52,10 +52,10 @@
52//usage:#define nameif_full_usage "\n\n" 52//usage:#define nameif_full_usage "\n\n"
53//usage: "Rename network interface while it in the down state." 53//usage: "Rename network interface while it in the down state."
54//usage: IF_NOT_FEATURE_NAMEIF_EXTENDED( 54//usage: IF_NOT_FEATURE_NAMEIF_EXTENDED(
55//usage: "\nThe device with address HWADDR is renamed to IFACE." 55//usage: "\nThe device with address HWADDR is renamed to IFNAME."
56//usage: ) 56//usage: )
57//usage: IF_FEATURE_NAMEIF_EXTENDED( 57//usage: IF_FEATURE_NAMEIF_EXTENDED(
58//usage: "\nThe device matched by SELECTOR is renamed to IFACE." 58//usage: "\nThe device matched by SELECTOR is renamed to IFNAME."
59//usage: "\nSELECTOR can be a combination of:" 59//usage: "\nSELECTOR can be a combination of:"
60//usage: "\n driver=STRING" 60//usage: "\n driver=STRING"
61//usage: "\n bus=STRING" 61//usage: "\n bus=STRING"
diff --git a/networking/telnetd.c b/networking/telnetd.c
index 29f805de7..de4d733f9 100644
--- a/networking/telnetd.c
+++ b/networking/telnetd.c
@@ -109,6 +109,7 @@
109//usage: "\n -i Inetd mode" 109//usage: "\n -i Inetd mode"
110//usage: IF_FEATURE_TELNETD_INETD_WAIT( 110//usage: IF_FEATURE_TELNETD_INETD_WAIT(
111//usage: "\n -w SEC Inetd 'wait' mode, linger time SEC" 111//usage: "\n -w SEC Inetd 'wait' mode, linger time SEC"
112//usage: "\n inetd.conf line: 23 stream tcp wait root telnetd telnetd -w10"
112//usage: "\n -S Log to syslog (implied by -i or without -F and -w)" 113//usage: "\n -S Log to syslog (implied by -i or without -F and -w)"
113//usage: ) 114//usage: )
114//usage: ) 115//usage: )
diff --git a/networking/tftp.c b/networking/tftp.c
index 4b86ed9de..f5b4367ca 100644
--- a/networking/tftp.c
+++ b/networking/tftp.c
@@ -113,10 +113,9 @@
113//usage:#define tftpd_full_usage "\n\n" 113//usage:#define tftpd_full_usage "\n\n"
114//usage: "Transfer a file on tftp client's request\n" 114//usage: "Transfer a file on tftp client's request\n"
115//usage: "\n" 115//usage: "\n"
116//usage: "tftpd should be used as an inetd service.\n" 116//usage: "tftpd is an inetd service, inetd.conf line:\n"
117//usage: "tftpd's line for inetd.conf:\n"
118//usage: " 69 dgram udp nowait root tftpd tftpd -l /files/to/serve\n" 117//usage: " 69 dgram udp nowait root tftpd tftpd -l /files/to/serve\n"
119//usage: "It also can be ran from udpsvd:\n" 118//usage: "Can be run from udpsvd:\n"
120//usage: " udpsvd -vE 0.0.0.0 69 tftpd /files/to/serve\n" 119//usage: " udpsvd -vE 0.0.0.0 69 tftpd /files/to/serve\n"
121//usage: "\n -r Prohibit upload" 120//usage: "\n -r Prohibit upload"
122//usage: "\n -c Allow file creation via upload" 121//usage: "\n -c Allow file creation via upload"
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index 7f288f891..8d11a7539 100644
--- a/networking/udhcp/d6_dhcpc.c
+++ b/networking/udhcp/d6_dhcpc.c
@@ -479,15 +479,15 @@ static ALWAYS_INLINE uint32_t random_xid(void)
479} 479}
480 480
481/* Initialize the packet with the proper defaults */ 481/* Initialize the packet with the proper defaults */
482static uint8_t *init_d6_packet(struct d6_packet *packet, char type, uint32_t xid) 482static uint8_t *init_d6_packet(struct d6_packet *packet, char type)
483{ 483{
484 uint8_t *ptr; 484 uint8_t *ptr;
485 unsigned secs; 485 unsigned secs;
486 486
487 memset(packet, 0, sizeof(*packet)); 487 memset(packet, 0, sizeof(*packet));
488 488
489 packet->d6_xid32 = xid; 489 packet->d6_xid32 = client_data.xid;
490 packet->d6_msg_type = type; 490 packet->d6_msg_type = type; /* union, overwrites lowest byte of d6_xid32 */
491 491
492 /* ELAPSED_TIME option is required to be present by the RFC, 492 /* ELAPSED_TIME option is required to be present by the RFC,
493 * and some servers do check for its presense. [which?] 493 * and some servers do check for its presense. [which?]
@@ -585,13 +585,13 @@ static int d6_mcast_from_client_data_ifindex(struct d6_packet *packet, uint8_t *
585 * about parameter values the client would like to have returned. 585 * about parameter values the client would like to have returned.
586 */ 586 */
587/* NOINLINE: limit stack usage in caller */ 587/* NOINLINE: limit stack usage in caller */
588static NOINLINE int send_d6_info_request(uint32_t xid) 588static NOINLINE int send_d6_info_request(void)
589{ 589{
590 struct d6_packet packet; 590 struct d6_packet packet;
591 uint8_t *opt_ptr; 591 uint8_t *opt_ptr;
592 592
593 /* Fill in: msg type */ 593 /* Fill in: msg type, xid, ELAPSED_TIME */
594 opt_ptr = init_d6_packet(&packet, D6_MSG_INFORMATION_REQUEST, xid); 594 opt_ptr = init_d6_packet(&packet, D6_MSG_INFORMATION_REQUEST);
595 595
596 /* Add options: client-id, 596 /* Add options: client-id,
597 * "param req" option according to -O, options specified with -x 597 * "param req" option according to -O, options specified with -x
@@ -684,14 +684,14 @@ static NOINLINE int send_d6_info_request(uint32_t xid)
684 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 684 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
685 */ 685 */
686/* NOINLINE: limit stack usage in caller */ 686/* NOINLINE: limit stack usage in caller */
687static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ipv6) 687static NOINLINE int send_d6_discover(struct in6_addr *requested_ipv6)
688{ 688{
689 struct d6_packet packet; 689 struct d6_packet packet;
690 uint8_t *opt_ptr; 690 uint8_t *opt_ptr;
691 unsigned len; 691 unsigned len;
692 692
693 /* Fill in: msg type */ 693 /* Fill in: msg type, xid, ELAPSED_TIME */
694 opt_ptr = init_d6_packet(&packet, D6_MSG_SOLICIT, xid); 694 opt_ptr = init_d6_packet(&packet, D6_MSG_SOLICIT);
695 695
696 /* Create new IA_NA, optionally with included IAADDR with requested IP */ 696 /* Create new IA_NA, optionally with included IAADDR with requested IP */
697 free(client6_data.ia_na); 697 free(client6_data.ia_na);
@@ -763,13 +763,13 @@ static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ip
763 * messages from the server. 763 * messages from the server.
764 */ 764 */
765/* NOINLINE: limit stack usage in caller */ 765/* NOINLINE: limit stack usage in caller */
766static NOINLINE int send_d6_select(uint32_t xid) 766static NOINLINE int send_d6_select(void)
767{ 767{
768 struct d6_packet packet; 768 struct d6_packet packet;
769 uint8_t *opt_ptr; 769 uint8_t *opt_ptr;
770 770
771 /* Fill in: msg type */ 771 /* Fill in: msg type, xid, ELAPSED_TIME */
772 opt_ptr = init_d6_packet(&packet, D6_MSG_REQUEST, xid); 772 opt_ptr = init_d6_packet(&packet, D6_MSG_REQUEST);
773 773
774 /* server id */ 774 /* server id */
775 opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2); 775 opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
@@ -836,13 +836,13 @@ static NOINLINE int send_d6_select(uint32_t xid)
836 * about parameter values the client would like to have returned. 836 * about parameter values the client would like to have returned.
837 */ 837 */
838/* NOINLINE: limit stack usage in caller */ 838/* NOINLINE: limit stack usage in caller */
839static NOINLINE int send_d6_renew(uint32_t xid, struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6) 839static NOINLINE int send_d6_renew(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
840{ 840{
841 struct d6_packet packet; 841 struct d6_packet packet;
842 uint8_t *opt_ptr; 842 uint8_t *opt_ptr;
843 843
844 /* Fill in: msg type */ 844 /* Fill in: msg type, xid, ELAPSED_TIME */
845 opt_ptr = init_d6_packet(&packet, DHCPREQUEST, xid); 845 opt_ptr = init_d6_packet(&packet, DHCPREQUEST);
846 846
847 /* server id */ 847 /* server id */
848 opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2); 848 opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
@@ -877,8 +877,8 @@ int send_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
877 uint8_t *opt_ptr; 877 uint8_t *opt_ptr;
878 struct option_set *ci; 878 struct option_set *ci;
879 879
880 /* Fill in: msg type, client id */ 880 /* Fill in: msg type, xid, ELAPSED_TIME */
881 opt_ptr = init_d6_packet(&packet, D6_MSG_RELEASE, random_xid()); 881 opt_ptr = init_d6_packet(&packet, D6_MSG_RELEASE);
882 /* server id */ 882 /* server id */
883 opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2); 883 opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
884 /* IA NA (contains our current IP) */ 884 /* IA NA (contains our current IP) */
@@ -1097,6 +1097,7 @@ static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *ou
1097 || client_data.state == RENEW_REQUESTED 1097 || client_data.state == RENEW_REQUESTED
1098 ) { 1098 ) {
1099 bb_simple_info_msg("unicasting a release"); 1099 bb_simple_info_msg("unicasting a release");
1100 client_data.xid = random_xid(); //TODO: can omit?
1100 send_d6_release(server_ipv6, our_cur_ipv6); /* unicast */ 1101 send_d6_release(server_ipv6, our_cur_ipv6); /* unicast */
1101 } 1102 }
1102 bb_simple_info_msg("entering released state"); 1103 bb_simple_info_msg("entering released state");
@@ -1126,23 +1127,23 @@ static void client_background(void)
1126//usage:# define IF_UDHCP_VERBOSE(...) 1127//usage:# define IF_UDHCP_VERBOSE(...)
1127//usage:#endif 1128//usage:#endif
1128//usage:#define udhcpc6_trivial_usage 1129//usage:#define udhcpc6_trivial_usage
1129//usage: "[-fbnq"IF_UDHCP_VERBOSE("v")"odR] [-i IFACE] [-r IPv6] [-s PROG] [-p PIDFILE]\n" 1130//usage: "[-fbq"IF_UDHCP_VERBOSE("v")"R] [-t N] [-T SEC] [-A SEC|-n] [-i IFACE] [-s PROG]\n"
1130//usage: " [-x OPT:VAL]... [-O OPT]..." IF_FEATURE_UDHCP_PORT(" [-P PORT]") 1131//usage: " [-p PIDFILE]"IF_FEATURE_UDHCP_PORT(" [-P PORT]")" [-ldo] [-r IPv6] [-x OPT:VAL]... [-O OPT]..."
1131//usage:#define udhcpc6_full_usage "\n" 1132//usage:#define udhcpc6_full_usage "\n"
1132//usage: "\n -i IFACE Interface to use (default "CONFIG_UDHCPC_DEFAULT_INTERFACE")" 1133//usage: "\n -i IFACE Interface to use (default "CONFIG_UDHCPC_DEFAULT_INTERFACE")"
1133//usage: "\n -p FILE Create pidfile" 1134//usage: "\n -p FILE Create pidfile"
1134//usage: "\n -s PROG Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")" 1135//usage: "\n -s PROG Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")"
1135//usage: "\n -B Request broadcast replies" 1136//usage: "\n -B Request broadcast replies"
1136//usage: "\n -t N Send up to N discover packets" 1137//usage: "\n -t N Send up to N discover packets"
1137//usage: "\n -T N Pause between packets (default 3 seconds)" 1138//usage: "\n -T SEC Pause between packets (default 3)"
1138//usage: "\n -A N Wait N seconds (default 20) after failure" 1139//usage: "\n -A SEC Wait if lease is not obtained (default 20)"
1139//usage: "\n -f Run in foreground"
1140//usage: USE_FOR_MMU( 1140//usage: USE_FOR_MMU(
1141//usage: "\n -b Background if lease is not obtained" 1141//usage: "\n -b Background if lease is not obtained"
1142//usage: ) 1142//usage: )
1143//usage: "\n -n Exit if lease is not obtained" 1143//usage: "\n -n Exit if lease is not obtained"
1144//usage: "\n -q Exit after obtaining lease" 1144//usage: "\n -q Exit after obtaining lease"
1145//usage: "\n -R Release IP on exit" 1145//usage: "\n -R Release IP on exit"
1146//usage: "\n -f Run in foreground"
1146//usage: "\n -S Log to syslog too" 1147//usage: "\n -S Log to syslog too"
1147//usage: IF_FEATURE_UDHCP_PORT( 1148//usage: IF_FEATURE_UDHCP_PORT(
1148//usage: "\n -P PORT Use PORT (default 546)" 1149//usage: "\n -P PORT Use PORT (default 546)"
@@ -1150,12 +1151,12 @@ static void client_background(void)
1150////usage: IF_FEATURE_UDHCPC_ARPING( 1151////usage: IF_FEATURE_UDHCPC_ARPING(
1151////usage: "\n -a Use arping to validate offered address" 1152////usage: "\n -a Use arping to validate offered address"
1152////usage: ) 1153////usage: )
1153//usage: "\n -O OPT Request option OPT from server (cumulative)"
1154//usage: "\n -o Don't request any options (unless -O is given)"
1155//usage: "\n -r IPv6 Request this address ('no' to not request any IP)"
1156//usage: "\n -d Request prefix"
1157//usage: "\n -l Send 'information request' instead of 'solicit'" 1154//usage: "\n -l Send 'information request' instead of 'solicit'"
1158//usage: "\n (used for servers which do not assign IPv6 addresses)" 1155//usage: "\n (used for servers which do not assign IPv6 addresses)"
1156//usage: "\n -r IPv6 Request this address ('no' to not request any IP)"
1157//usage: "\n -d Request prefix"
1158//usage: "\n -o Don't request any options (unless -O is given)"
1159//usage: "\n -O OPT Request option OPT from server (cumulative)"
1159//usage: "\n -x OPT:VAL Include option OPT in sent packets (cumulative)" 1160//usage: "\n -x OPT:VAL Include option OPT in sent packets (cumulative)"
1160//usage: "\n Examples of string, numeric, and hex byte opts:" 1161//usage: "\n Examples of string, numeric, and hex byte opts:"
1161//usage: "\n -x hostname:bbox - option 12" 1162//usage: "\n -x hostname:bbox - option 12"
@@ -1183,7 +1184,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1183 struct in6_addr srv6_buf; 1184 struct in6_addr srv6_buf;
1184 struct in6_addr ipv6_buf; 1185 struct in6_addr ipv6_buf;
1185 struct in6_addr *requested_ipv6; 1186 struct in6_addr *requested_ipv6;
1186 uint32_t xid = 0;
1187 int packet_num; 1187 int packet_num;
1188 int timeout; /* must be signed */ 1188 int timeout; /* must be signed */
1189 int lease_remaining; /* must be signed */ 1189 int lease_remaining; /* must be signed */
@@ -1387,13 +1387,13 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1387 if (!discover_retries || packet_num < discover_retries) { 1387 if (!discover_retries || packet_num < discover_retries) {
1388 if (packet_num == 0) { 1388 if (packet_num == 0) {
1389 change_listen_mode(LISTEN_RAW); 1389 change_listen_mode(LISTEN_RAW);
1390 xid = random_xid(); 1390 client_data.xid = random_xid();
1391 } 1391 }
1392 /* multicast */ 1392 /* multicast */
1393 if (opt & OPT_l) 1393 if (opt & OPT_l)
1394 send_d6_info_request(xid); 1394 send_d6_info_request();
1395 else 1395 else
1396 send_d6_discover(xid, requested_ipv6); 1396 send_d6_discover(requested_ipv6);
1397 timeout = discover_timeout; 1397 timeout = discover_timeout;
1398 packet_num++; 1398 packet_num++;
1399 continue; 1399 continue;
@@ -1427,7 +1427,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1427 case REQUESTING: 1427 case REQUESTING:
1428 if (!discover_retries || packet_num < discover_retries) { 1428 if (!discover_retries || packet_num < discover_retries) {
1429 /* send multicast select packet */ 1429 /* send multicast select packet */
1430 send_d6_select(xid); 1430 send_d6_select();
1431 timeout = discover_timeout; 1431 timeout = discover_timeout;
1432 packet_num++; 1432 packet_num++;
1433 continue; 1433 continue;
@@ -1459,9 +1459,9 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1459 * into INIT_SELECTING state. 1459 * into INIT_SELECTING state.
1460 */ 1460 */
1461 if (opt & OPT_l) 1461 if (opt & OPT_l)
1462 send_d6_info_request(xid); 1462 send_d6_info_request();
1463 else 1463 else
1464 send_d6_renew(xid, &srv6_buf, requested_ipv6); 1464 send_d6_renew(&srv6_buf, requested_ipv6);
1465 timeout = discover_timeout; 1465 timeout = discover_timeout;
1466 packet_num++; 1466 packet_num++;
1467 continue; 1467 continue;
@@ -1484,9 +1484,9 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1484 * try to find DHCP server using broadcast */ 1484 * try to find DHCP server using broadcast */
1485 if (lease_remaining > 0 && packet_num < 3) { 1485 if (lease_remaining > 0 && packet_num < 3) {
1486 if (opt & OPT_l) 1486 if (opt & OPT_l)
1487 send_d6_info_request(xid); 1487 send_d6_info_request();
1488 else /* send a broadcast renew request */ 1488 else /* send a broadcast renew request */
1489 send_d6_renew(xid, /*server_ipv6:*/ NULL, requested_ipv6); 1489 send_d6_renew(/*server_ipv6:*/ NULL, requested_ipv6);
1490 timeout = discover_timeout; 1490 timeout = discover_timeout;
1491 packet_num++; 1491 packet_num++;
1492 continue; 1492 continue;
@@ -1581,9 +1581,9 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1581 packet_end = (uint8_t*)&packet + len; 1581 packet_end = (uint8_t*)&packet + len;
1582 } 1582 }
1583 1583
1584 if ((packet.d6_xid32 & htonl(0x00ffffff)) != xid) { 1584 if ((packet.d6_xid32 & htonl(0x00ffffff)) != client_data.xid) {
1585 log1("xid %x (our is %x)%s", 1585 log1("xid %x (our is %x)%s",
1586 (unsigned)(packet.d6_xid32 & htonl(0x00ffffff)), (unsigned)xid, 1586 (unsigned)(packet.d6_xid32 & htonl(0x00ffffff)), (unsigned)client_data.xid,
1587 ", ignoring packet" 1587 ", ignoring packet"
1588 ); 1588 );
1589 continue; 1589 continue;
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index 4e3d8ca5e..331f13a8c 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -605,7 +605,7 @@ static void init_packet(struct dhcp_packet *packet, char type)
605 /* Fill in: op, htype, hlen, cookie fields; message type option: */ 605 /* Fill in: op, htype, hlen, cookie fields; message type option: */
606 udhcp_init_header(packet, type); 606 udhcp_init_header(packet, type);
607 607
608 packet->xid = random_xid(); 608 packet->xid = client_data.xid;
609 609
610 client_data.last_secs = monotonic_sec(); 610 client_data.last_secs = monotonic_sec();
611 if (client_data.first_secs == 0) 611 if (client_data.first_secs == 0)
@@ -663,6 +663,24 @@ static void add_client_options(struct dhcp_packet *packet)
663 // ...add (DHCP_VENDOR, "udhcp "BB_VER) opt... 663 // ...add (DHCP_VENDOR, "udhcp "BB_VER) opt...
664} 664}
665 665
666static void add_serverid_and_clientid_options(struct dhcp_packet *packet, uint32_t server)
667{
668 struct option_set *ci;
669
670 udhcp_add_simple_option(packet, DHCP_SERVER_ID, server);
671
672 /* RFC 2131 section 2:
673 * If the client uses a 'client identifier' in one message,
674 * it MUST use that same identifier in all subsequent messages.
675 * section 3.1.6:
676 * If the client used a 'client identifier' when it obtained the lease,
677 * it MUST use the same 'client identifier' in the DHCPRELEASE message.
678 */
679 ci = udhcp_find_option(client_data.options, DHCP_CLIENT_ID);
680 if (ci)
681 udhcp_add_binary_option(packet, ci->data);
682}
683
666/* RFC 2131 684/* RFC 2131
667 * 4.4.4 Use of broadcast and unicast 685 * 4.4.4 Use of broadcast and unicast
668 * 686 *
@@ -701,17 +719,15 @@ static int bcast_or_ucast(struct dhcp_packet *packet, uint32_t ciaddr, uint32_t
701 719
702/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */ 720/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */
703/* NOINLINE: limit stack usage in caller */ 721/* NOINLINE: limit stack usage in caller */
704static NOINLINE int send_discover(uint32_t xid, uint32_t requested) 722static NOINLINE int send_discover(uint32_t requested)
705{ 723{
706 struct dhcp_packet packet; 724 struct dhcp_packet packet;
707 725
708 /* Fill in: op, htype, hlen, cookie, chaddr fields, 726 /* Fill in: op, htype, hlen, cookie, chaddr fields,
709 * random xid field (we override it below), 727 * xid field, message type option:
710 * message type option:
711 */ 728 */
712 init_packet(&packet, DHCPDISCOVER); 729 init_packet(&packet, DHCPDISCOVER);
713 730
714 packet.xid = xid;
715 if (requested) 731 if (requested)
716 udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested); 732 udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
717 733
@@ -729,7 +745,7 @@ static NOINLINE int send_discover(uint32_t xid, uint32_t requested)
729 * "The client _broadcasts_ a DHCPREQUEST message..." 745 * "The client _broadcasts_ a DHCPREQUEST message..."
730 */ 746 */
731/* NOINLINE: limit stack usage in caller */ 747/* NOINLINE: limit stack usage in caller */
732static NOINLINE int send_select(uint32_t xid, uint32_t server, uint32_t requested) 748static NOINLINE int send_select(uint32_t server, uint32_t requested)
733{ 749{
734 struct dhcp_packet packet; 750 struct dhcp_packet packet;
735 struct in_addr temp_addr; 751 struct in_addr temp_addr;
@@ -748,18 +764,16 @@ static NOINLINE int send_select(uint32_t xid, uint32_t server, uint32_t requeste
748 * include that list in all subsequent messages. 764 * include that list in all subsequent messages.
749 */ 765 */
750 /* Fill in: op, htype, hlen, cookie, chaddr fields, 766 /* Fill in: op, htype, hlen, cookie, chaddr fields,
751 * random xid field (we override it below), 767 * xid field, message type option:
752 * message type option:
753 */ 768 */
754 init_packet(&packet, DHCPREQUEST); 769 init_packet(&packet, DHCPREQUEST);
755 770
756 packet.xid = xid;
757 udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested); 771 udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
758 772
759 udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server); 773 udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
760 774
761 /* Add options: maxsize, 775 /* Add options: maxsize,
762 * "param req" option according to -O, and options specified with -x 776 * "param req" option according to -O, options specified with -x
763 */ 777 */
764 add_client_options(&packet); 778 add_client_options(&packet);
765 779
@@ -775,7 +789,7 @@ static NOINLINE int send_select(uint32_t xid, uint32_t server, uint32_t requeste
775 789
776/* Unicast or broadcast a DHCP renew message */ 790/* Unicast or broadcast a DHCP renew message */
777/* NOINLINE: limit stack usage in caller */ 791/* NOINLINE: limit stack usage in caller */
778static NOINLINE int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr) 792static NOINLINE int send_renew(uint32_t server, uint32_t ciaddr)
779{ 793{
780 struct dhcp_packet packet; 794 struct dhcp_packet packet;
781 795
@@ -794,16 +808,14 @@ static NOINLINE int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr)
794 * replying to the client. 808 * replying to the client.
795 */ 809 */
796 /* Fill in: op, htype, hlen, cookie, chaddr fields, 810 /* Fill in: op, htype, hlen, cookie, chaddr fields,
797 * random xid field (we override it below), 811 * xid field, message type option:
798 * message type option:
799 */ 812 */
800 init_packet(&packet, DHCPREQUEST); 813 init_packet(&packet, DHCPREQUEST);
801 814
802 packet.xid = xid;
803 packet.ciaddr = ciaddr; 815 packet.ciaddr = ciaddr;
804 816
805 /* Add options: maxsize, 817 /* Add options: maxsize,
806 * "param req" option according to -O, and options specified with -x 818 * "param req" option according to -O, options specified with -x
807 */ 819 */
808 add_client_options(&packet); 820 add_client_options(&packet);
809 821
@@ -821,7 +833,7 @@ static NOINLINE int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr)
821#if ENABLE_FEATURE_UDHCPC_ARPING 833#if ENABLE_FEATURE_UDHCPC_ARPING
822/* Broadcast a DHCP decline message */ 834/* Broadcast a DHCP decline message */
823/* NOINLINE: limit stack usage in caller */ 835/* NOINLINE: limit stack usage in caller */
824static NOINLINE int send_decline(/*uint32_t xid,*/ uint32_t server, uint32_t requested) 836static NOINLINE int send_decline(uint32_t server, uint32_t requested)
825{ 837{
826 struct dhcp_packet packet; 838 struct dhcp_packet packet;
827 839
@@ -830,20 +842,10 @@ static NOINLINE int send_decline(/*uint32_t xid,*/ uint32_t server, uint32_t req
830 */ 842 */
831 init_packet(&packet, DHCPDECLINE); 843 init_packet(&packet, DHCPDECLINE);
832 844
833#if 0
834 /* RFC 2131 says DHCPDECLINE's xid is randomly selected by client,
835 * but in case the server is buggy and wants DHCPDECLINE's xid
836 * to match the xid which started entire handshake,
837 * we use the same xid we used in initial DHCPDISCOVER:
838 */
839 packet.xid = xid;
840#endif
841 /* DHCPDECLINE uses "requested ip", not ciaddr, to store offered IP */ 845 /* DHCPDECLINE uses "requested ip", not ciaddr, to store offered IP */
842 udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested); 846 udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested);
843 847
844 udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server); 848 add_serverid_and_clientid_options(&packet, server);
845
846//TODO: add client-id opt?
847 849
848 bb_simple_info_msg("broadcasting decline"); 850 bb_simple_info_msg("broadcasting decline");
849 return raw_bcast_from_client_data_ifindex(&packet, INADDR_ANY); 851 return raw_bcast_from_client_data_ifindex(&packet, INADDR_ANY);
@@ -856,7 +858,6 @@ ALWAYS_INLINE /* one caller, help compiler to use this fact */
856int send_release(uint32_t server, uint32_t ciaddr) 858int send_release(uint32_t server, uint32_t ciaddr)
857{ 859{
858 struct dhcp_packet packet; 860 struct dhcp_packet packet;
859 struct option_set *ci;
860 861
861 /* Fill in: op, htype, hlen, cookie, chaddr, random xid fields, 862 /* Fill in: op, htype, hlen, cookie, chaddr, random xid fields,
862 * message type option: 863 * message type option:
@@ -866,15 +867,7 @@ int send_release(uint32_t server, uint32_t ciaddr)
866 /* DHCPRELEASE uses ciaddr, not "requested ip", to store IP being released */ 867 /* DHCPRELEASE uses ciaddr, not "requested ip", to store IP being released */
867 packet.ciaddr = ciaddr; 868 packet.ciaddr = ciaddr;
868 869
869 udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server); 870 add_serverid_and_clientid_options(&packet, server);
870
871 /* RFC 2131 section 3.1.6:
872 * If the client used a 'client identifier' when it obtained the lease,
873 * it MUST use the same 'client identifier' in the DHCPRELEASE message.
874 */
875 ci = udhcp_find_option(client_data.options, DHCP_CLIENT_ID);
876 if (ci)
877 udhcp_add_binary_option(&packet, ci->data);
878 871
879 bb_info_msg("sending %s", "release"); 872 bb_info_msg("sending %s", "release");
880 /* Note: normally we unicast here since "server" is not zero. 873 /* Note: normally we unicast here since "server" is not zero.
@@ -1120,7 +1113,7 @@ static void change_listen_mode(int new_mode)
1120 /* else LISTEN_NONE: client_data.sockfd stays closed */ 1113 /* else LISTEN_NONE: client_data.sockfd stays closed */
1121} 1114}
1122 1115
1123static void perform_release(uint32_t server_addr, uint32_t requested_ip) 1116static void perform_release(uint32_t server_id, uint32_t requested_ip)
1124{ 1117{
1125 char buffer[sizeof("255.255.255.255")]; 1118 char buffer[sizeof("255.255.255.255")];
1126 struct in_addr temp_addr; 1119 struct in_addr temp_addr;
@@ -1133,12 +1126,13 @@ static void perform_release(uint32_t server_addr, uint32_t requested_ip)
1133 || client_data.state == REBINDING 1126 || client_data.state == REBINDING
1134 || client_data.state == RENEW_REQUESTED 1127 || client_data.state == RENEW_REQUESTED
1135 ) { 1128 ) {
1136 temp_addr.s_addr = server_addr; 1129 temp_addr.s_addr = server_id;
1137 strcpy(buffer, inet_ntoa(temp_addr)); 1130 strcpy(buffer, inet_ntoa(temp_addr));
1138 temp_addr.s_addr = requested_ip; 1131 temp_addr.s_addr = requested_ip;
1139 bb_info_msg("unicasting a release of %s to %s", 1132 bb_info_msg("unicasting a release of %s to %s",
1140 inet_ntoa(temp_addr), buffer); 1133 inet_ntoa(temp_addr), buffer);
1141 send_release(server_addr, requested_ip); /* unicast */ 1134 client_data.xid = random_xid(); //TODO: can omit?
1135 send_release(server_id, requested_ip); /* unicast */
1142 } 1136 }
1143 bb_simple_info_msg("entering released state"); 1137 bb_simple_info_msg("entering released state");
1144/* 1138/*
@@ -1167,7 +1161,7 @@ static void client_background(void)
1167//usage:# define IF_UDHCP_VERBOSE(...) 1161//usage:# define IF_UDHCP_VERBOSE(...)
1168//usage:#endif 1162//usage:#endif
1169//usage:#define udhcpc_trivial_usage 1163//usage:#define udhcpc_trivial_usage
1170//usage: "[-fbq"IF_UDHCP_VERBOSE("v")"RB]"IF_FEATURE_UDHCPC_ARPING(" [-a[MSEC]]")" [-t N] [-T SEC] [-A SEC/-n]\n" 1164//usage: "[-fbq"IF_UDHCP_VERBOSE("v")"RB]"IF_FEATURE_UDHCPC_ARPING(" [-a[MSEC]]")" [-t N] [-T SEC] [-A SEC|-n]\n"
1171//usage: " [-i IFACE]"IF_FEATURE_UDHCP_PORT(" [-P PORT]")" [-s PROG] [-p PIDFILE]\n" 1165//usage: " [-i IFACE]"IF_FEATURE_UDHCP_PORT(" [-P PORT]")" [-s PROG] [-p PIDFILE]\n"
1172//usage: " [-oC] [-r IP] [-V VENDOR] [-F NAME] [-x OPT:VAL]... [-O OPT]..." 1166//usage: " [-oC] [-r IP] [-V VENDOR] [-F NAME] [-x OPT:VAL]... [-O OPT]..."
1173//usage:#define udhcpc_full_usage "\n" 1167//usage:#define udhcpc_full_usage "\n"
@@ -1224,9 +1218,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1224 int tryagain_timeout = 20; 1218 int tryagain_timeout = 20;
1225 int discover_timeout = 3; 1219 int discover_timeout = 3;
1226 int discover_retries = 3; 1220 int discover_retries = 3;
1227 uint32_t server_addr = server_addr; /* for compiler */ 1221 uint32_t server_id = server_id; /* for compiler */
1228 uint32_t requested_ip = 0; 1222 uint32_t requested_ip = 0;
1229 uint32_t xid = xid; /* for compiler */
1230 int packet_num; 1223 int packet_num;
1231 int timeout; /* must be signed */ 1224 int timeout; /* must be signed */
1232 int lease_remaining; /* must be signed */ 1225 int lease_remaining; /* must be signed */
@@ -1291,7 +1284,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1291 memcpy(p + OPT_DATA + 3, str_F, len); /* do not store NUL byte */ 1284 memcpy(p + OPT_DATA + 3, str_F, len); /* do not store NUL byte */
1292 } 1285 }
1293 if (opt & OPT_r) 1286 if (opt & OPT_r)
1294 requested_ip = inet_addr(str_r); 1287 if (!inet_aton(str_r, (void*)&requested_ip))
1288 bb_show_usage();
1295#if ENABLE_FEATURE_UDHCP_PORT 1289#if ENABLE_FEATURE_UDHCP_PORT
1296 if (opt & OPT_P) { 1290 if (opt & OPT_P) {
1297 CLIENT_PORT = xatou16(str_P); 1291 CLIENT_PORT = xatou16(str_P);
@@ -1451,10 +1445,10 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1451 if (!discover_retries || packet_num < discover_retries) { 1445 if (!discover_retries || packet_num < discover_retries) {
1452 if (packet_num == 0) { 1446 if (packet_num == 0) {
1453 change_listen_mode(LISTEN_RAW); 1447 change_listen_mode(LISTEN_RAW);
1454 xid = random_xid(); 1448 client_data.xid = random_xid();
1455 } 1449 }
1456 /* broadcast */ 1450 /* broadcast */
1457 send_discover(xid, requested_ip); 1451 send_discover(requested_ip);
1458 timeout = discover_timeout; 1452 timeout = discover_timeout;
1459 packet_num++; 1453 packet_num++;
1460 continue; 1454 continue;
@@ -1488,7 +1482,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1488 case REQUESTING: 1482 case REQUESTING:
1489 if (packet_num < 3) { 1483 if (packet_num < 3) {
1490 /* send broadcast select packet */ 1484 /* send broadcast select packet */
1491 send_select(xid, server_addr, requested_ip); 1485 send_select(server_id, requested_ip);
1492 timeout = discover_timeout; 1486 timeout = discover_timeout;
1493 packet_num++; 1487 packet_num++;
1494 continue; 1488 continue;
@@ -1519,7 +1513,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1519 * Anyway, it does recover by eventually failing through 1513 * Anyway, it does recover by eventually failing through
1520 * into INIT_SELECTING state. 1514 * into INIT_SELECTING state.
1521 */ 1515 */
1522 if (send_renew(xid, server_addr, requested_ip) >= 0) { 1516 if (send_renew(server_id, requested_ip) >= 0) {
1523 timeout = discover_timeout; 1517 timeout = discover_timeout;
1524 packet_num++; 1518 packet_num++;
1525 continue; 1519 continue;
@@ -1548,7 +1542,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1548 * try to find DHCP server using broadcast */ 1542 * try to find DHCP server using broadcast */
1549 if (lease_remaining > 0 && packet_num < 3) { 1543 if (lease_remaining > 0 && packet_num < 3) {
1550 /* send a broadcast renew request */ 1544 /* send a broadcast renew request */
1551 send_renew(xid, 0 /*INADDR_ANY*/, requested_ip); 1545 send_renew(0 /*INADDR_ANY*/, requested_ip);
1552 timeout = discover_timeout; 1546 timeout = discover_timeout;
1553 packet_num++; 1547 packet_num++;
1554 continue; 1548 continue;
@@ -1610,7 +1604,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1610 timeout = 0; 1604 timeout = 0;
1611 continue; 1605 continue;
1612 case SIGUSR2: 1606 case SIGUSR2:
1613 perform_release(server_addr, requested_ip); 1607 perform_release(server_id, requested_ip);
1614 /* ^^^ switches to LISTEN_NONE */ 1608 /* ^^^ switches to LISTEN_NONE */
1615 timeout = INT_MAX; 1609 timeout = INT_MAX;
1616 continue; 1610 continue;
@@ -1641,9 +1635,9 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1641 continue; 1635 continue;
1642 } 1636 }
1643 1637
1644 if (packet.xid != xid) { 1638 if (packet.xid != client_data.xid) {
1645 log1("xid %x (our is %x)%s", 1639 log1("xid %x (our is %x)%s",
1646 (unsigned)packet.xid, (unsigned)xid, 1640 (unsigned)packet.xid, (unsigned)client_data.xid,
1647 ", ignoring packet" 1641 ", ignoring packet"
1648 ); 1642 );
1649 continue; 1643 continue;
@@ -1695,13 +1689,13 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1695 * They either supply DHCP_SERVER_ID of 0.0.0.0 or don't supply it at all. 1689 * They either supply DHCP_SERVER_ID of 0.0.0.0 or don't supply it at all.
1696 * They say ISC DHCP client supports this case. 1690 * They say ISC DHCP client supports this case.
1697 */ 1691 */
1698 server_addr = 0; 1692 server_id = 0;
1699 temp = udhcp_get_option32(&packet, DHCP_SERVER_ID); 1693 temp = udhcp_get_option32(&packet, DHCP_SERVER_ID);
1700 if (!temp) { 1694 if (!temp) {
1701 bb_simple_info_msg("no server ID, using 0.0.0.0"); 1695 bb_simple_info_msg("no server ID, using 0.0.0.0");
1702 } else { 1696 } else {
1703 /* it IS unaligned sometimes, don't "optimize" */ 1697 /* it IS unaligned sometimes, don't "optimize" */
1704 move_from_unaligned32(server_addr, temp); 1698 move_from_unaligned32(server_id, temp);
1705 } 1699 }
1706 /*xid = packet.xid; - already is */ 1700 /*xid = packet.xid; - already is */
1707 temp_addr.s_addr = requested_ip = packet.yiaddr; 1701 temp_addr.s_addr = requested_ip = packet.yiaddr;
@@ -1725,7 +1719,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1725 1719
1726 change_listen_mode(LISTEN_NONE); 1720 change_listen_mode(LISTEN_NONE);
1727 1721
1728 temp_addr.s_addr = server_addr; 1722 temp_addr.s_addr = server_id;
1729 strcpy(server_str, inet_ntoa(temp_addr)); 1723 strcpy(server_str, inet_ntoa(temp_addr));
1730 temp_addr.s_addr = packet.yiaddr; 1724 temp_addr.s_addr = packet.yiaddr;
1731 1725
@@ -1772,7 +1766,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1772 ) { 1766 ) {
1773 bb_simple_info_msg("offered address is in use " 1767 bb_simple_info_msg("offered address is in use "
1774 "(got ARP reply), declining"); 1768 "(got ARP reply), declining");
1775 send_decline(/*xid,*/ server_addr, packet.yiaddr); 1769 client_data.xid = random_xid(); //TODO: can omit?
1770 send_decline(server_id, packet.yiaddr);
1776 1771
1777 if (client_data.state != REQUESTING) 1772 if (client_data.state != REQUESTING)
1778 d4_run_script_deconfig(); 1773 d4_run_script_deconfig();
@@ -1807,7 +1802,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1807 timeout = (unsigned)lease_remaining / 2; 1802 timeout = (unsigned)lease_remaining / 2;
1808 client_data.state = BOUND; 1803 client_data.state = BOUND;
1809 /* make future renew packets use different xid */ 1804 /* make future renew packets use different xid */
1810 /* xid = random_xid(); ...but why bother? */ 1805 /* client_data.xid = random_xid(); ...but why bother? */
1811 packet_num = 0; 1806 packet_num = 0;
1812 continue; /* back to main loop */ 1807 continue; /* back to main loop */
1813 } 1808 }
@@ -1816,20 +1811,14 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1816 * "wrong" server can reply first, with a NAK. 1811 * "wrong" server can reply first, with a NAK.
1817 * Do not interpret it as a NAK from "our" server. 1812 * Do not interpret it as a NAK from "our" server.
1818 */ 1813 */
1819 if (server_addr != 0) { 1814 uint32_t svid = 0; /* we treat no server id as 0.0.0.0 */
1820 uint32_t svid; 1815 uint8_t *temp = udhcp_get_option32(&packet, DHCP_SERVER_ID);
1821 uint8_t *temp; 1816 if (temp)
1822
1823 temp = udhcp_get_option32(&packet, DHCP_SERVER_ID);
1824 if (!temp) {
1825 non_matching_svid:
1826 log1("received DHCP NAK with wrong"
1827 " server ID%s", ", ignoring packet");
1828 continue;
1829 }
1830 move_from_unaligned32(svid, temp); 1817 move_from_unaligned32(svid, temp);
1831 if (svid != server_addr) 1818 if (svid != server_id) {
1832 goto non_matching_svid; 1819 log1("received DHCP NAK with wrong"
1820 " server ID%s", ", ignoring packet");
1821 continue;
1833 } 1822 }
1834 /* return to init state */ 1823 /* return to init state */
1835 change_listen_mode(LISTEN_NONE); 1824 change_listen_mode(LISTEN_NONE);
@@ -1853,7 +1842,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1853 1842
1854 ret0: 1843 ret0:
1855 if (opt & OPT_R) /* release on quit */ 1844 if (opt & OPT_R) /* release on quit */
1856 perform_release(server_addr, requested_ip); 1845 perform_release(server_id, requested_ip);
1857 retval = 0; 1846 retval = 0;
1858 ret: 1847 ret:
1859 /*if (client_data.pidfile) - remove_pidfile has its own check */ 1848 /*if (client_data.pidfile) - remove_pidfile has its own check */
diff --git a/networking/udhcp/dhcpc.h b/networking/udhcp/dhcpc.h
index cd9ead6bd..19b054b32 100644
--- a/networking/udhcp/dhcpc.h
+++ b/networking/udhcp/dhcpc.h
@@ -11,6 +11,7 @@ struct client_data_t {
11 uint8_t client_mac[6]; /* Our mac address */ 11 uint8_t client_mac[6]; /* Our mac address */
12 IF_FEATURE_UDHCP_PORT(uint16_t port;) 12 IF_FEATURE_UDHCP_PORT(uint16_t port;)
13 int ifindex; /* Index number of the interface to use */ 13 int ifindex; /* Index number of the interface to use */
14 uint32_t xid;
14 uint8_t opt_mask[256 / 8]; /* Bitmask of options to send (-O option) */ 15 uint8_t opt_mask[256 / 8]; /* Bitmask of options to send (-O option) */
15// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TODO: DHCPv6 has 16-bit option numbers 16// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TODO: DHCPv6 has 16-bit option numbers
16 const char *interface; /* The name of the interface to use */ 17 const char *interface; /* The name of the interface to use */
diff --git a/networking/vconfig.c b/networking/vconfig.c
index 4f1fbe280..7e805be9c 100644
--- a/networking/vconfig.c
+++ b/networking/vconfig.c
@@ -20,12 +20,12 @@
20//usage: "COMMAND [OPTIONS]" 20//usage: "COMMAND [OPTIONS]"
21//usage:#define vconfig_full_usage "\n\n" 21//usage:#define vconfig_full_usage "\n\n"
22//usage: "Create and remove virtual ethernet devices\n" 22//usage: "Create and remove virtual ethernet devices\n"
23//usage: "\n add IFACE VLAN_ID" 23//usage: "\n add IFACE VLAN_ID"
24//usage: "\n rem VLAN_NAME" 24//usage: "\n rem VLAN_NAME"
25//usage: "\n set_flag IFACE 0|1 VLAN_QOS" 25//usage: "\n set_flag IFACE 0|1 VLAN_QOS"
26//usage: "\n set_egress_map VLAN_NAME SKB_PRIO VLAN_QOS" 26//usage: "\n set_egress_map VLAN_NAME SKB_PRIO VLAN_QOS"
27//usage: "\n set_ingress_map VLAN_NAME SKB_PRIO VLAN_QOS" 27//usage: "\n set_ingress_map VLAN_NAME SKB_PRIO VLAN_QOS"
28//usage: "\n set_name_type NAME_TYPE" 28//usage: "\n set_name_type NAME_TYPE"
29 29
30#include "libbb.h" 30#include "libbb.h"
31#include <net/if.h> 31#include <net/if.h>
diff --git a/procps/free.c b/procps/free.c
index 142786083..f19c38dd5 100644
--- a/procps/free.c
+++ b/procps/free.c
@@ -19,9 +19,9 @@
19//kbuild:lib-$(CONFIG_FREE) += free.o 19//kbuild:lib-$(CONFIG_FREE) += free.o
20 20
21//usage:#define free_trivial_usage 21//usage:#define free_trivial_usage
22//usage: "" IF_DESKTOP("[-b/k/m/g]") 22//usage: "" IF_DESKTOP("[-bkmgh]")
23//usage:#define free_full_usage "\n\n" 23//usage:#define free_full_usage "\n\n"
24//usage: "Display the amount of free and used system memory" 24//usage: "Display free and used memory"
25//usage: 25//usage:
26//usage:#define free_example_usage 26//usage:#define free_example_usage
27//usage: "$ free\n" 27//usage: "$ free\n"
@@ -29,6 +29,27 @@
29//usage: " Mem: 257628 248724 8904 59644 93124\n" 29//usage: " Mem: 257628 248724 8904 59644 93124\n"
30//usage: " Swap: 128516 8404 120112\n" 30//usage: " Swap: 128516 8404 120112\n"
31//usage: "Total: 386144 257128 129016\n" 31//usage: "Total: 386144 257128 129016\n"
32//procps-ng 3.3.15:
33// -b, --bytes show output in bytes
34// --kilo show output in kilobytes
35// --mega show output in megabytes
36// --giga show output in gigabytes
37// --tera show output in terabytes
38// --peta show output in petabytes
39// -k, --kibi show output in kibibytes
40// -m, --mebi show output in mebibytes
41// -g, --gibi show output in gibibytes
42// --tebi show output in tebibytes
43// --pebi show output in pebibytes
44// -h, --human show human-readable output
45// --si use powers of 1000 not 1024
46// -l, --lohi show detailed low and high memory statistics
47// -t, --total show total for RAM + swap
48// -s N, --seconds N repeat printing every N seconds
49// -c N, --count N repeat printing N times, then exit
50// -w, --wide wide output
51//
52//NB: if we implement -s or -c, need to stop being NOFORK!
32 53
33#include "libbb.h" 54#include "libbb.h"
34#ifdef __linux__ 55#ifdef __linux__
@@ -38,18 +59,22 @@
38struct globals { 59struct globals {
39 unsigned mem_unit; 60 unsigned mem_unit;
40#if ENABLE_DESKTOP 61#if ENABLE_DESKTOP
41 uint8_t unit_steps; 62 unsigned unit;
42# define G_unit_steps g->unit_steps 63# define G_unit g->unit
43#else 64#else
44# define G_unit_steps 10 65# define G_unit (1 << 10)
45#endif 66#endif
46 unsigned long cached_kb, available_kb, reclaimable_kb; 67 unsigned long cached_kb, available_kb, reclaimable_kb;
47}; 68};
48/* Because of NOFORK, "globals" are not in global data */ 69/* Because of NOFORK, "globals" are not in global data */
49 70
50static unsigned long long scale(struct globals *g, unsigned long d) 71static const char *scale(struct globals *g, unsigned long d)
51{ 72{
52 return ((unsigned long long)d * g->mem_unit) >> G_unit_steps; 73 /* Display (size * block_size) with one decimal digit.
74 * If display_unit == 0, show value no bigger than 1024 with suffix (K,M,G...),
75 * else divide by display_unit and do not use suffix.
76 * Returns "auto pointer" */
77 return make_human_readable_str(d, g->mem_unit, G_unit);
53} 78}
54 79
55#if !ENABLE_PLATFORM_MINGW32 80#if !ENABLE_PLATFORM_MINGW32
@@ -97,20 +122,27 @@ int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM))
97 int seen_available; 122 int seen_available;
98 123
99#if ENABLE_DESKTOP 124#if ENABLE_DESKTOP
100 G.unit_steps = 10; 125 G.unit = 1 << 10;
101 if (argv[1] && argv[1][0] == '-') { 126 if (argv[1] && argv[1][0] == '-') {
102 switch (argv[1][1]) { 127 switch (argv[1][1]) {
103 case 'b': 128 case 'b':
104 G.unit_steps = 0; 129 G.unit = 1;
105 break; 130 break;
106 case 'k': /* 2^10 */ 131 case 'k': /* 2^10 */
107 /* G.unit_steps = 10; - already is */ 132 /* G.unit = 1 << 10; - already is */
108 break; 133 break;
109 case 'm': /* 2^20 */ 134 case 'm': /* 2^20 */
110 G.unit_steps = 20; 135 G.unit = 1 << 20;
111 break; 136 break;
112 case 'g': /* 2^30 */ 137 case 'g': /* 2^30 */
113 G.unit_steps = 30; 138 G.unit = 1 << 30;
139 break;
140// case 't':
141// -- WRONG, -t is not "terabytes" in procps-ng, it's --total
142// G.unit = 1 << 40;
143// break;
144 case 'h':
145 G.unit = 0; /* human readable */
114 break; 146 break;
115 default: 147 default:
116 bb_show_usage(); 148 bb_show_usage();
@@ -135,29 +167,13 @@ int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM))
135 cached += ((unsigned long long) G.reclaimable_kb * 1024) / G.mem_unit; 167 cached += ((unsigned long long) G.reclaimable_kb * 1024) / G.mem_unit;
136 cached_plus_free = cached + info.freeram; 168 cached_plus_free = cached + info.freeram;
137 169
138/* In case (long long * G.mem_unit) can overflow, this can be used to reduce the chances */ 170 printf("%12s%12s%12s",
139#if 0 //ENABLE_DESKTOP
140 while (!(G.mem_unit & 1) && G.unit_steps != 0) {
141 G.mem_unit >>= 1;
142 G.unit_steps--;
143 //bb_error_msg("mem_unit:%d unit_steps:%d", G.mem_unit, G.unit_steps);
144 }
145#endif
146
147#if !ENABLE_PLATFORM_MINGW32
148#define FIELDS_6 "%12llu %11llu %11llu %11llu %11llu %11llu\n"
149#define FIELDS_3 (FIELDS_6 + 6 + 7 + 7)
150#define FIELDS_2 (FIELDS_6 + 6 + 7 + 7 + 7)
151#else
152#define FIELDS_6 "%12I64u %11I64u %11I64u %11I64u %11I64u %11I64u\n"
153#define FIELDS_3 (FIELDS_6 + 7 + 8 + 8)
154#define FIELDS_2 (FIELDS_6 + 8 + 8 + 8 + 8)
155#endif
156
157 printf(FIELDS_6,
158 scale(&G, info.totalram), //total 171 scale(&G, info.totalram), //total
159 scale(&G, info.totalram - cached_plus_free), //used 172 scale(&G, info.totalram - cached_plus_free), //used
160 scale(&G, info.freeram), //free 173 scale(&G, info.freeram) //free
174 );
175 /* using two printf's: only 4 auto strings are supported, we need 6 */
176 printf("%12s%12s%12s\n",
161 scale(&G, info.sharedram), //shared 177 scale(&G, info.sharedram), //shared
162 scale(&G, cached), //buff/cache 178 scale(&G, cached), //buff/cache
163 scale(&G, available) //available 179 scale(&G, available) //available
@@ -167,14 +183,14 @@ int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM))
167 * buffer cache as free memory. */ 183 * buffer cache as free memory. */
168 if (!seen_available) { 184 if (!seen_available) {
169 printf("-/+ buffers/cache: "); 185 printf("-/+ buffers/cache: ");
170 printf(FIELDS_2, 186 printf("%12s%12s%12s\n" + 4,
171 scale(&G, info.totalram - cached_plus_free), //used 187 scale(&G, info.totalram - cached_plus_free), //used
172 scale(&G, cached_plus_free) //free 188 scale(&G, cached_plus_free) //free
173 ); 189 );
174 } 190 }
175#if BB_MMU 191#if BB_MMU
176 printf("Swap: "); 192 printf("Swap: ");
177 printf(FIELDS_3, 193 printf("%12s%12s%12s\n",
178 scale(&G, info.totalswap), //total 194 scale(&G, info.totalswap), //total
179 scale(&G, info.totalswap - info.freeswap), //used 195 scale(&G, info.totalswap - info.freeswap), //used
180 scale(&G, info.freeswap) //free 196 scale(&G, info.freeswap) //free
diff --git a/procps/kill.c b/procps/kill.c
index 358d8f42b..8c2bc2b6f 100644
--- a/procps/kill.c
+++ b/procps/kill.c
@@ -59,7 +59,7 @@
59//usage: "$ kill 252\n" 59//usage: "$ kill 252\n"
60//usage: 60//usage:
61//usage:#define killall_trivial_usage 61//usage:#define killall_trivial_usage
62//usage: "[-l] [-q] [-SIG] PROCESS_NAME..." 62//usage: "[-lq] [-SIG] PROCESS_NAME..."
63//usage:#define killall_full_usage "\n\n" 63//usage:#define killall_full_usage "\n\n"
64//usage: "Send a signal (default: TERM) to given processes\n" 64//usage: "Send a signal (default: TERM) to given processes\n"
65//usage: "\n -l List all signal names and numbers" 65//usage: "\n -l List all signal names and numbers"
diff --git a/procps/lsof.c b/procps/lsof.c
index 21ac85ed3..9cb8d066c 100644
--- a/procps/lsof.c
+++ b/procps/lsof.c
@@ -66,7 +66,7 @@ int lsof_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
66 66
67 safe_strncpy(name + baseofs, entry->d_name, 10); 67 safe_strncpy(name + baseofs, entry->d_name, 10);
68 if ((fdlink = xmalloc_readlink(name)) != NULL) { 68 if ((fdlink = xmalloc_readlink(name)) != NULL) {
69 printf("%d\t%s\t%s\n", proc->pid, proc->exe, fdlink); 69 printf("%d\t%s\t%s\t%s\n", proc->pid, proc->exe, entry->d_name, fdlink);
70 free(fdlink); 70 free(fdlink);
71 } 71 }
72 } 72 }
diff --git a/procps/pgrep.c b/procps/pgrep.c
index 495e0ef9d..6d25c247e 100644
--- a/procps/pgrep.c
+++ b/procps/pgrep.c
@@ -44,17 +44,17 @@
44//usage: "\n -P Match parent process ID" 44//usage: "\n -P Match parent process ID"
45//usage: 45//usage:
46//usage:#define pkill_trivial_usage 46//usage:#define pkill_trivial_usage
47//usage: "[-l|-SIGNAL] [-fnovx] [-s SID|-P PPID|PATTERN]" 47//usage: "[-l|-SIGNAL] [-xfvno] [-s SID|-P PPID|PATTERN]"
48//usage:#define pkill_full_usage "\n\n" 48//usage:#define pkill_full_usage "\n\n"
49//usage: "Send a signal to process(es) selected by regex PATTERN\n" 49//usage: "Send signal to processes selected by regex PATTERN\n"
50//usage: "\n -l List all signals" 50//usage: "\n -l List all signals"
51//usage: "\n -x Match whole name (not substring)"
51//usage: "\n -f Match against entire command line" 52//usage: "\n -f Match against entire command line"
53//usage: "\n -s SID Match session ID (0 for current)"
54//usage: "\n -P PPID Match parent process ID"
55//usage: "\n -v Negate the match"
52//usage: "\n -n Signal the newest process only" 56//usage: "\n -n Signal the newest process only"
53//usage: "\n -o Signal the oldest process only" 57//usage: "\n -o Signal the oldest process only"
54//usage: "\n -v Negate the match"
55//usage: "\n -x Match whole name (not substring)"
56//usage: "\n -s Match session ID (0 for current)"
57//usage: "\n -P Match parent process ID"
58 58
59#include "libbb.h" 59#include "libbb.h"
60#include "xregex.h" 60#include "xregex.h"
diff --git a/procps/sysctl.c b/procps/sysctl.c
index e16b119e9..40afa0c90 100644
--- a/procps/sysctl.c
+++ b/procps/sysctl.c
@@ -21,16 +21,16 @@
21//kbuild:lib-$(CONFIG_BB_SYSCTL) += sysctl.o 21//kbuild:lib-$(CONFIG_BB_SYSCTL) += sysctl.o
22 22
23//usage:#define sysctl_trivial_usage 23//usage:#define sysctl_trivial_usage
24//usage: "-p [-enq] [FILE...] / [-enqaw] [KEY[=VALUE]]..." 24//usage: "[-enq] { -a | -p [FILE]... | [-w] [KEY[=VALUE]]... }"
25//usage:#define sysctl_full_usage "\n\n" 25//usage:#define sysctl_full_usage "\n\n"
26//usage: "Show/set kernel parameters\n" 26//usage: "Show/set kernel parameters\n"
27//usage: "\n -p Set values from FILEs (default /etc/sysctl.conf)"
28//usage: "\n -e Don't warn about unknown keys" 27//usage: "\n -e Don't warn about unknown keys"
29//usage: "\n -n Don't show key names" 28//usage: "\n -n Don't show key names"
30//usage: "\n -q Quiet" 29//usage: "\n -q Quiet"
31//usage: "\n -a Show all values" 30//usage: "\n -a Show all values"
32/* Same as -a, no need to show it */ 31/* Same as -a, no need to show it */
33/* //usage: "\n -A Show all values in table form" */ 32/* //usage: "\n -A Show all values in table form" */
33//usage: "\n -p Set values from FILEs (default /etc/sysctl.conf)"
34//usage: "\n -w Set values" 34//usage: "\n -w Set values"
35//usage: 35//usage:
36//usage:#define sysctl_example_usage 36//usage:#define sysctl_example_usage
diff --git a/procps/top.c b/procps/top.c
index cadc4ecec..4cd545c69 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -1052,9 +1052,9 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1052//usage: "[-b"IF_FEATURE_TOPMEM("m")IF_FEATURE_SHOW_THREADS("H")"]" 1052//usage: "[-b"IF_FEATURE_TOPMEM("m")IF_FEATURE_SHOW_THREADS("H")"]"
1053//usage: " [-n COUNT] [-d SECONDS]" 1053//usage: " [-n COUNT] [-d SECONDS]"
1054//usage:#define top_full_usage "\n\n" 1054//usage:#define top_full_usage "\n\n"
1055//usage: "Provide a view of process activity in real time." 1055//usage: "Show a view of process activity in real time."
1056//usage: "\n""Read the status of all processes from /proc each SECONDS" 1056//usage: "\n""Read the status of all processes from /proc each SECONDS"
1057//usage: "\n""and display a screenful of them." 1057//usage: "\n""and show a screenful of them."
1058//usage: "\n" 1058//usage: "\n"
1059//usage: IF_FEATURE_TOP_INTERACTIVE( 1059//usage: IF_FEATURE_TOP_INTERACTIVE(
1060//usage: "Keys:" 1060//usage: "Keys:"
diff --git a/procps/watch.c b/procps/watch.c
index 059eb1dda..1190b29df 100644
--- a/procps/watch.c
+++ b/procps/watch.c
@@ -22,7 +22,7 @@
22//usage: "[-n SEC] [-t] PROG ARGS" 22//usage: "[-n SEC] [-t] PROG ARGS"
23//usage:#define watch_full_usage "\n\n" 23//usage:#define watch_full_usage "\n\n"
24//usage: "Run PROG periodically\n" 24//usage: "Run PROG periodically\n"
25//usage: "\n -n SEC Loop period (default 2)" 25//usage: "\n -n SEC Period (default 2)"
26//usage: "\n -t Don't print header" 26//usage: "\n -t Don't print header"
27//usage: 27//usage:
28//usage:#define watch_example_usage 28//usage:#define watch_example_usage
diff --git a/runit/svlogd.c b/runit/svlogd.c
index 02c305696..f7576f0fa 100644
--- a/runit/svlogd.c
+++ b/runit/svlogd.c
@@ -140,12 +140,12 @@ log message, you can use a pattern like this instead
140//usage:#define svlogd_full_usage "\n\n" 140//usage:#define svlogd_full_usage "\n\n"
141//usage: "Read log data from stdin and write to rotated log files in DIRs" 141//usage: "Read log data from stdin and write to rotated log files in DIRs"
142//usage: "\n" 142//usage: "\n"
143//usage: "\n""-r C Replace non-printable characters with C" 143//usage: "\n"" -r C Replace non-printable characters with C"
144//usage: "\n""-R CHARS Also replace CHARS with C (default _)" 144//usage: "\n"" -R CHARS Also replace CHARS with C (default _)"
145//usage: "\n""-t Timestamp with @tai64n" 145//usage: "\n"" -t Timestamp with @tai64n"
146//usage: "\n""-tt Timestamp with yyyy-mm-dd_hh:mm:ss.sssss" 146//usage: "\n"" -tt Timestamp with yyyy-mm-dd_hh:mm:ss.sssss"
147//usage: "\n""-ttt Timestamp with yyyy-mm-ddThh:mm:ss.sssss" 147//usage: "\n"" -ttt Timestamp with yyyy-mm-ddThh:mm:ss.sssss"
148//usage: "\n""-v Verbose" 148//usage: "\n"" -v Verbose"
149//usage: "\n" 149//usage: "\n"
150//usage: "\n""DIR/config file modifies behavior:" 150//usage: "\n""DIR/config file modifies behavior:"
151//usage: "\n""sSIZE - when to rotate logs (default 1000000, 0 disables)" 151//usage: "\n""sSIZE - when to rotate logs (default 1000000, 0 disables)"
diff --git a/shell/ash.c b/shell/ash.c
index bab6138da..4ae42595d 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -13572,7 +13572,7 @@ parsesub: {
13572 do { 13572 do {
13573 STPUTC(c, out); 13573 STPUTC(c, out);
13574 c = pgetc_eatbnl(); 13574 c = pgetc_eatbnl();
13575 } while (!subtype && isdigit(c)); 13575 } while ((subtype == 0 || subtype == VSLENGTH) && isdigit(c));
13576 } else if (c != '}') { 13576 } else if (c != '}') {
13577 /* $[{[#]]<specialchar>[}] */ 13577 /* $[{[#]]<specialchar>[}] */
13578 int cc = c; 13578 int cc = c;
@@ -13602,11 +13602,6 @@ parsesub: {
13602 } else 13602 } else
13603 goto badsub; 13603 goto badsub;
13604 13604
13605 if (c != '}' && subtype == VSLENGTH) {
13606 /* ${#VAR didn't end with } */
13607 goto badsub;
13608 }
13609
13610 if (subtype == 0) { 13605 if (subtype == 0) {
13611 static const char types[] ALIGN1 = "}-+?="; 13606 static const char types[] ALIGN1 = "}-+?=";
13612 /* ${VAR...} but not $VAR or ${#VAR} */ 13607 /* ${VAR...} but not $VAR or ${#VAR} */
@@ -13663,6 +13658,8 @@ parsesub: {
13663#endif 13658#endif
13664 } 13659 }
13665 } else { 13660 } else {
13661 if (subtype == VSLENGTH && c != '}')
13662 subtype = 0;
13666 badsub: 13663 badsub:
13667 pungetc(); 13664 pungetc();
13668 } 13665 }
@@ -15358,7 +15355,7 @@ init(void)
15358 15355
15359 15356
15360//usage:#define ash_trivial_usage 15357//usage:#define ash_trivial_usage
15361//usage: "[-il] [-/+Cabefmnuvx] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS] / -s [ARGS]]" 15358//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE [ARGS] | -s [ARGS]]"
15362//////// comes from ^^^^^^^^^^optletters 15359//////// comes from ^^^^^^^^^^optletters
15363//usage:#define ash_full_usage "\n\n" 15360//usage:#define ash_full_usage "\n\n"
15364//usage: "Unix shell interpreter" 15361//usage: "Unix shell interpreter"
diff --git a/shell/ash_test/ash-misc/control_char3.right b/shell/ash_test/ash-misc/control_char3.right
new file mode 100644
index 000000000..283e02cbb
--- /dev/null
+++ b/shell/ash_test/ash-misc/control_char3.right
@@ -0,0 +1 @@
SHELL: line 1: : not found
diff --git a/shell/ash_test/ash-misc/control_char3.tests b/shell/ash_test/ash-misc/control_char3.tests
new file mode 100755
index 000000000..4359db3f3
--- /dev/null
+++ b/shell/ash_test/ash-misc/control_char3.tests
@@ -0,0 +1,2 @@
1# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages)
2$THIS_SH -c '\' SHELL
diff --git a/shell/ash_test/ash-misc/control_char4.right b/shell/ash_test/ash-misc/control_char4.right
new file mode 100644
index 000000000..2bf18e684
--- /dev/null
+++ b/shell/ash_test/ash-misc/control_char4.right
@@ -0,0 +1 @@
SHELL: line 1: -: not found
diff --git a/shell/ash_test/ash-misc/control_char4.tests b/shell/ash_test/ash-misc/control_char4.tests
new file mode 100755
index 000000000..48010f154
--- /dev/null
+++ b/shell/ash_test/ash-misc/control_char4.tests
@@ -0,0 +1,2 @@
1# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages)
2$THIS_SH -c '"-"' SHELL
diff --git a/shell/ash_test/ash-parsing/bkslash_newline4.right b/shell/ash_test/ash-parsing/bkslash_newline4.right
new file mode 100644
index 000000000..2110716d1
--- /dev/null
+++ b/shell/ash_test/ash-parsing/bkslash_newline4.right
@@ -0,0 +1,4 @@
11:1
222:22
33:3
4Ok:0
diff --git a/shell/ash_test/ash-parsing/bkslash_newline4.tests b/shell/ash_test/ash-parsing/bkslash_newline4.tests
new file mode 100755
index 000000000..c8f5037c4
--- /dev/null
+++ b/shell/ash_test/ash-parsing/bkslash_newline4.tests
@@ -0,0 +1,14 @@
1set -- 1 22 333
2echo 1:$\
31
4echo 22:$\
5{\
62\
7}
8echo 3:$\
9{\
10#\
113\
12}
13echo Ok:$\
14?
diff --git a/shell/hush.c b/shell/hush.c
index 77921e11c..1aa0a400d 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -339,7 +339,7 @@
339 * therefore we don't show them either. 339 * therefore we don't show them either.
340 */ 340 */
341//usage:#define hush_trivial_usage 341//usage:#define hush_trivial_usage
342//usage: "[-enxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS] / -s [ARGS]]" 342//usage: "[-enxl] [-c 'SCRIPT' [ARG0 ARGS] | FILE [ARGS] | -s [ARGS]]"
343//usage:#define hush_full_usage "\n\n" 343//usage:#define hush_full_usage "\n\n"
344//usage: "Unix shell interpreter" 344//usage: "Unix shell interpreter"
345 345
@@ -3696,9 +3696,10 @@ static void debug_print_tree(struct pipe *pi, int lvl)
3696 3696
3697 pin = 0; 3697 pin = 0;
3698 while (pi) { 3698 while (pi) {
3699 fdprintf(2, "%*spipe %d %sres_word=%s followup=%d %s\n", 3699 fdprintf(2, "%*spipe %d #cmds:%d %sres_word=%s followup=%d %s\n",
3700 lvl*2, "", 3700 lvl*2, "",
3701 pin, 3701 pin,
3702 pi->num_cmds,
3702 (IF_HAS_KEYWORDS(pi->pi_inverted ? "! " :) ""), 3703 (IF_HAS_KEYWORDS(pi->pi_inverted ? "! " :) ""),
3703 RES[pi->res_word], 3704 RES[pi->res_word],
3704 pi->followup, PIPE[pi->followup] 3705 pi->followup, PIPE[pi->followup]
@@ -3841,6 +3842,9 @@ static void done_pipe(struct parse_context *ctx, pipe_style type)
3841#endif 3842#endif
3842 /* Replace all pipes in ctx with one newly created */ 3843 /* Replace all pipes in ctx with one newly created */
3843 ctx->list_head = ctx->pipe = pi; 3844 ctx->list_head = ctx->pipe = pi;
3845 /* for cases like "cmd && &", do not be tricked by last command
3846 * being null - the entire {...} & is NOT null! */
3847 not_null = 1;
3844 } else { 3848 } else {
3845 no_conv: 3849 no_conv:
3846 ctx->pipe->followup = type; 3850 ctx->pipe->followup = type;
@@ -4994,6 +4998,32 @@ static int parse_dollar(o_string *as_string,
4994 * which check invalid constructs like ${%}. 4998 * which check invalid constructs like ${%}.
4995 * Oh well... let's check that the var name part is fine... */ 4999 * Oh well... let's check that the var name part is fine... */
4996 5000
5001 if (isdigit(len_single_ch)
5002 || (len_single_ch == '#' && isdigit(i_peek_and_eat_bkslash_nl(input)))
5003 ) {
5004 /* Execution engine uses plain xatoi_positive()
5005 * to interpret ${NNN} and {#NNN},
5006 * check syntax here in the parser.
5007 * (bash does not support expressions in ${#NN},
5008 * e.g. ${#$var} and {#1:+WORD} are not supported).
5009 */
5010 unsigned cnt = 9; /* max 9 digits for ${NN} and 8 for {#NN} */
5011 while (1) {
5012 o_addchr(dest, ch);
5013 debug_printf_parse(": '%c'\n", ch);
5014 ch = i_getch_and_eat_bkslash_nl(input);
5015 nommu_addchr(as_string, ch);
5016 if (ch == '}')
5017 break;
5018 if (--cnt == 0)
5019 goto bad_dollar_syntax;
5020 if (len_single_ch != '#' && strchr(VAR_SUBST_OPS, ch))
5021 /* ${NN<op>...} is valid */
5022 goto eat_until_closing;
5023 if (!isdigit(ch))
5024 goto bad_dollar_syntax;
5025 }
5026 } else
4997 while (1) { 5027 while (1) {
4998 unsigned pos; 5028 unsigned pos;
4999 5029
@@ -5004,7 +5034,6 @@ static int parse_dollar(o_string *as_string,
5004 nommu_addchr(as_string, ch); 5034 nommu_addchr(as_string, ch);
5005 if (ch == '}') 5035 if (ch == '}')
5006 break; 5036 break;
5007
5008 if (!isalnum(ch) && ch != '_') { 5037 if (!isalnum(ch) && ch != '_') {
5009 unsigned end_ch; 5038 unsigned end_ch;
5010 unsigned char last_ch; 5039 unsigned char last_ch;
@@ -5023,6 +5052,7 @@ static int parse_dollar(o_string *as_string,
5023 * special var name, e.g. ${#!}. 5052 * special var name, e.g. ${#!}.
5024 */ 5053 */
5025 } 5054 }
5055 eat_until_closing:
5026 /* Eat everything until closing '}' (or ':') */ 5056 /* Eat everything until closing '}' (or ':') */
5027 end_ch = '}'; 5057 end_ch = '}';
5028 if (BASH_SUBSTR 5058 if (BASH_SUBSTR
@@ -5237,6 +5267,11 @@ static int encode_string(o_string *as_string,
5237 } 5267 }
5238#endif 5268#endif
5239 o_addQchr(dest, ch); 5269 o_addQchr(dest, ch);
5270 if (ch == SPECIAL_VAR_SYMBOL) {
5271 /* Convert "^C" to corresponding special variable reference */
5272 o_addchr(dest, SPECIAL_VAR_QUOTED_SVS);
5273 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5274 }
5240 goto again; 5275 goto again;
5241#undef as_string 5276#undef as_string
5242} 5277}
@@ -5348,6 +5383,11 @@ static struct pipe *parse_stream(char **pstring,
5348 if (ch == '\n') 5383 if (ch == '\n')
5349 continue; /* drop \<newline>, get next char */ 5384 continue; /* drop \<newline>, get next char */
5350 nommu_addchr(&ctx.as_string, '\\'); 5385 nommu_addchr(&ctx.as_string, '\\');
5386 if (ch == SPECIAL_VAR_SYMBOL) {
5387 nommu_addchr(&ctx.as_string, ch);
5388 /* Convert \^C to corresponding special variable reference */
5389 goto case_SPECIAL_VAR_SYMBOL;
5390 }
5351 o_addchr(&ctx.word, '\\'); 5391 o_addchr(&ctx.word, '\\');
5352 if (ch == EOF) { 5392 if (ch == EOF) {
5353 /* Testcase: eval 'echo Ok\' */ 5393 /* Testcase: eval 'echo Ok\' */
@@ -5672,6 +5712,7 @@ static struct pipe *parse_stream(char **pstring,
5672 /* Note: nommu_addchr(&ctx.as_string, ch) is already done */ 5712 /* Note: nommu_addchr(&ctx.as_string, ch) is already done */
5673 5713
5674 switch (ch) { 5714 switch (ch) {
5715 case_SPECIAL_VAR_SYMBOL:
5675 case SPECIAL_VAR_SYMBOL: 5716 case SPECIAL_VAR_SYMBOL:
5676 /* Convert raw ^C to corresponding special variable reference */ 5717 /* Convert raw ^C to corresponding special variable reference */
5677 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL); 5718 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
diff --git a/shell/hush_test/hush-misc/control_char3.right b/shell/hush_test/hush-misc/control_char3.right
new file mode 100644
index 000000000..94b4f8699
--- /dev/null
+++ b/shell/hush_test/hush-misc/control_char3.right
@@ -0,0 +1 @@
hush: can't execute '': No such file or directory
diff --git a/shell/hush_test/hush-misc/control_char3.tests b/shell/hush_test/hush-misc/control_char3.tests
new file mode 100755
index 000000000..4359db3f3
--- /dev/null
+++ b/shell/hush_test/hush-misc/control_char3.tests
@@ -0,0 +1,2 @@
1# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages)
2$THIS_SH -c '\' SHELL
diff --git a/shell/hush_test/hush-misc/control_char4.right b/shell/hush_test/hush-misc/control_char4.right
new file mode 100644
index 000000000..698e21427
--- /dev/null
+++ b/shell/hush_test/hush-misc/control_char4.right
@@ -0,0 +1 @@
hush: can't execute '-': No such file or directory
diff --git a/shell/hush_test/hush-misc/control_char4.tests b/shell/hush_test/hush-misc/control_char4.tests
new file mode 100755
index 000000000..48010f154
--- /dev/null
+++ b/shell/hush_test/hush-misc/control_char4.tests
@@ -0,0 +1,2 @@
1# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages)
2$THIS_SH -c '"-"' SHELL
diff --git a/shell/hush_test/hush-parsing/bkslash_newline4.right b/shell/hush_test/hush-parsing/bkslash_newline4.right
new file mode 100644
index 000000000..2110716d1
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bkslash_newline4.right
@@ -0,0 +1,4 @@
11:1
222:22
33:3
4Ok:0
diff --git a/shell/hush_test/hush-parsing/bkslash_newline4.tests b/shell/hush_test/hush-parsing/bkslash_newline4.tests
new file mode 100755
index 000000000..c8f5037c4
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bkslash_newline4.tests
@@ -0,0 +1,14 @@
1set -- 1 22 333
2echo 1:$\
31
4echo 22:$\
5{\
62\
7}
8echo 3:$\
9{\
10#\
113\
12}
13echo Ok:$\
14?
diff --git a/shell/hush_test/hush-vars/var6.right b/shell/hush_test/hush-vars/var6.right
index 40e67fdf5..5e28d2fab 100644
--- a/shell/hush_test/hush-vars/var6.right
+++ b/shell/hush_test/hush-vars/var6.right
@@ -1,2 +1,2 @@
1hush: invalid number '1q' 1hush: syntax error: unterminated ${name}
2hush: syntax error: unterminated ${name} 2hush: syntax error: unterminated ${name}
diff --git a/sysklogd/logger.c b/sysklogd/logger.c
index 9422b6ea7..04b2c8e3b 100644
--- a/sysklogd/logger.c
+++ b/sysklogd/logger.c
@@ -26,7 +26,7 @@
26//usage: "Write MESSAGE (or stdin) to syslog\n" 26//usage: "Write MESSAGE (or stdin) to syslog\n"
27//usage: "\n -s Log to stderr as well as the system log" 27//usage: "\n -s Log to stderr as well as the system log"
28//usage: "\n -t TAG Log using the specified tag (defaults to user name)" 28//usage: "\n -t TAG Log using the specified tag (defaults to user name)"
29//usage: "\n -p PRIO Priority (numeric or facility.level pair)" 29//usage: "\n -p PRIO Priority (number or FACILITY.LEVEL pair)"
30//usage: 30//usage:
31//usage:#define logger_example_usage 31//usage:#define logger_example_usage
32//usage: "$ logger \"hello\"\n" 32//usage: "$ logger \"hello\"\n"
diff --git a/testsuite/mv/mv-files-to-dir-2 b/testsuite/mv/mv-files-to-dir-2
new file mode 100644
index 000000000..e189ebb6f
--- /dev/null
+++ b/testsuite/mv/mv-files-to-dir-2
@@ -0,0 +1,16 @@
1echo file number one > file1
2echo file number two > file2
3ln -s file2 link1
4mkdir dir1
5TZ=UTC0 touch -d '2000-01-30 05:24:08' dir1/file3
6mkdir there
7busybox mv -t there file1 file2 link1 dir1
8test -f there/file1
9test -f there/file2
10test -f there/dir1/file3
11test -L there/link1
12test xfile2 = x`readlink there/link1`
13test ! -e file1
14test ! -e file2
15test ! -e link1
16test ! -e dir1/file3
diff --git a/testsuite/unlzma.tests b/testsuite/unlzma.tests
index 0e98afe09..fcc6e9441 100755
--- a/testsuite/unlzma.tests
+++ b/testsuite/unlzma.tests
@@ -8,14 +8,23 @@
8 8
9# Damaged encrypted streams 9# Damaged encrypted streams
10testing "unlzma (bad archive 1)" \ 10testing "unlzma (bad archive 1)" \
11 "unlzma <unlzma_issue_1.lzma >/dev/null; echo \$?" \ 11 "unlzma <unlzma_issue_1.lzma 2>&1 >/dev/null; echo \$?" \
12"1 12"unlzma: corrupted data
131
13" "" "" 14" "" ""
14 15
15# Damaged encrypted streams 16# Damaged encrypted streams
16testing "unlzma (bad archive 2)" \ 17testing "unlzma (bad archive 2)" \
17 "unlzma <unlzma_issue_2.lzma >/dev/null; echo \$?" \ 18 "unlzma <unlzma_issue_2.lzma 2>&1 >/dev/null; echo \$?" \
18"1 19"unlzma: corrupted data
201
21" "" ""
22
23# Damaged encrypted streams
24testing "unlzma (bad archive 3)" \
25 "unlzma <unlzma_issue_3.lzma 2>&1 >/dev/null; echo \$?" \
26"unlzma: corrupted data
271
19" "" "" 28" "" ""
20 29
21exit $FAILCOUNT 30exit $FAILCOUNT
diff --git a/testsuite/unlzma_issue_3.lzma b/testsuite/unlzma_issue_3.lzma
new file mode 100644
index 000000000..cc60f29e4
--- /dev/null
+++ b/testsuite/unlzma_issue_3.lzma
Binary files differ
diff --git a/util-linux/blockdev.c b/util-linux/blockdev.c
index 20a031377..3b550220a 100644
--- a/util-linux/blockdev.c
+++ b/util-linux/blockdev.c
@@ -25,8 +25,11 @@
25//usage: "\n --getbsz Get block size" 25//usage: "\n --getbsz Get block size"
26//usage: "\n --setbsz BYTES Set block size" 26//usage: "\n --setbsz BYTES Set block size"
27//usage: "\n --getsz Get device size in 512-byte sectors" 27//usage: "\n --getsz Get device size in 512-byte sectors"
28/*//usage: "\n --getsize Get device size in sectors (deprecated)"*/ 28///////: "\n --getsize Get device size in sectors (deprecated)"
29///////^^^^^ supported, but not shown in help ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29//usage: "\n --getsize64 Get device size in bytes" 30//usage: "\n --getsize64 Get device size in bytes"
31//usage: "\n --getra Get readahead in 512-byte sectors"
32//usage: "\n --setra SECTORS Set readahead"
30//usage: "\n --flushbufs Flush buffers" 33//usage: "\n --flushbufs Flush buffers"
31//usage: "\n --rereadpt Reread partition table" 34//usage: "\n --rereadpt Reread partition table"
32// util-linux 2.31 also has: 35// util-linux 2.31 also has:
@@ -57,6 +60,9 @@ static const char bdcmd_names[] ALIGN1 =
57 "getsz" "\0" 60 "getsz" "\0"
58 "getsize" "\0" 61 "getsize" "\0"
59 "getsize64" "\0" 62 "getsize64" "\0"
63 "getra" "\0"
64 "setra" "\0"
65#define CMD_SETRA 10
60 "flushbufs" "\0" 66 "flushbufs" "\0"
61 "rereadpt" "\0" 67 "rereadpt" "\0"
62; 68;
@@ -70,6 +76,8 @@ static const uint32_t bdcmd_ioctl[] ALIGN4 = {
70 BLKGETSIZE64, //getsz 76 BLKGETSIZE64, //getsz
71 BLKGETSIZE, //getsize 77 BLKGETSIZE, //getsize
72 BLKGETSIZE64, //getsize64 78 BLKGETSIZE64, //getsize64
79 BLKRAGET, //getra
80 BLKRASET, //setra
73 BLKFLSBUF, //flushbufs 81 BLKFLSBUF, //flushbufs
74 BLKRRPART, //rereadpt 82 BLKRRPART, //rereadpt
75}; 83};
@@ -95,6 +103,8 @@ static const uint8_t bdcmd_flags[] ALIGN1 = {
95 ARG_U64 + FL_SCALE512, //getsz 103 ARG_U64 + FL_SCALE512, //getsz
96 ARG_ULONG, //getsize 104 ARG_ULONG, //getsize
97 ARG_U64, //getsize64 105 ARG_U64, //getsize64
106 ARG_ULONG, //getra
107 ARG_ULONG + FL_NORESULT, //setra
98 ARG_NONE + FL_NORESULT, //flushbufs 108 ARG_NONE + FL_NORESULT, //flushbufs
99 ARG_NONE + FL_NORESULT, //rereadpt 109 ARG_NONE + FL_NORESULT, //rereadpt
100}; 110};
@@ -130,8 +140,9 @@ int blockdev_main(int argc UNUSED_PARAM, char **argv)
130 /* setrw translates to BLKROSET(0), most other ioctls don't care... */ 140 /* setrw translates to BLKROSET(0), most other ioctls don't care... */
131 /* ...setro will do BLKROSET(1) */ 141 /* ...setro will do BLKROSET(1) */
132 u64 = (bdcmd == CMD_SETRO); 142 u64 = (bdcmd == CMD_SETRO);
133 if (bdcmd == CMD_SETBSZ) { 143 if (bdcmd == CMD_SETBSZ || bdcmd == CMD_SETRA) {
134 /* ...setbsz is BLKBSZSET(bytes) */ 144 /* ...setbsz is BLKBSZSET(bytes) */
145 /* ...setra is BLKRASET(512 bytes) */
135 u64 = xatoi_positive(*++argv); 146 u64 = xatoi_positive(*++argv);
136 } 147 }
137 148
@@ -145,8 +156,11 @@ int blockdev_main(int argc UNUSED_PARAM, char **argv)
145#if BB_BIG_ENDIAN 156#if BB_BIG_ENDIAN
146 /* Store data properly wrt data size. 157 /* Store data properly wrt data size.
147 * (1) It's no-op for little-endian. 158 * (1) It's no-op for little-endian.
148 * (2) it's no-op for 0 and -1. Only --setro uses arg != 0 and != -1, 159 * (2) it's no-op for 0 and -1.
149 * and it is ARG_INT. --setbsz USER_VAL is also ARG_INT. 160 * --setro uses arg != 0 and != -1, and it is ARG_INT
161 * --setbsz USER_VAL is also ARG_INT
162 * --setra USER_VAL is ARG_ULONG, but it is passed by value,
163 * not reference (see below in ioctl call).
150 * Thus, we don't need to handle ARG_ULONG. 164 * Thus, we don't need to handle ARG_ULONG.
151 */ 165 */
152 switch (flags & ARG_MASK) { 166 switch (flags & ARG_MASK) {
@@ -161,7 +175,11 @@ int blockdev_main(int argc UNUSED_PARAM, char **argv)
161 } 175 }
162#endif 176#endif
163 177
164 if (ioctl(fd, bdcmd_ioctl[bdcmd], &ioctl_val_on_stack.u64) == -1) 178 if (ioctl(fd, bdcmd_ioctl[bdcmd],
179 bdcmd == CMD_SETRA
180 ? (void*)(uintptr_t)u64 /* BLKRASET passes _value_, not pointer to it */
181 : &ioctl_val_on_stack.u64
182 ) == -1)
165 bb_simple_perror_msg_and_die(*argv); 183 bb_simple_perror_msg_and_die(*argv);
166 184
167 /* Fetch it into register(s) */ 185 /* Fetch it into register(s) */
diff --git a/util-linux/dmesg.c b/util-linux/dmesg.c
index dc4e57169..6670b84de 100644
--- a/util-linux/dmesg.c
+++ b/util-linux/dmesg.c
@@ -46,7 +46,7 @@
46//kbuild:lib-$(CONFIG_DMESG) += dmesg.o 46//kbuild:lib-$(CONFIG_DMESG) += dmesg.o
47 47
48//usage:#define dmesg_trivial_usage 48//usage:#define dmesg_trivial_usage
49//usage: "[-c] [-n LEVEL] [-s SIZE]" 49//usage: "[-cr] [-n LEVEL] [-s SIZE]"
50//usage:#define dmesg_full_usage "\n\n" 50//usage:#define dmesg_full_usage "\n\n"
51//usage: "Print or control the kernel ring buffer\n" 51//usage: "Print or control the kernel ring buffer\n"
52//usage: "\n -c Clear ring buffer after printing" 52//usage: "\n -c Clear ring buffer after printing"
diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c
index c50ceead1..1c2a7d683 100644
--- a/util-linux/fdisk.c
+++ b/util-linux/fdisk.c
@@ -232,8 +232,8 @@ struct pte {
232}; 232};
233 233
234#define unable_to_open "can't open '%s'" 234#define unable_to_open "can't open '%s'"
235#define unable_to_read "can't read from %s" 235#define unable_to_read "can't read '%s'"
236#define unable_to_seek "can't seek on %s" 236#define unable_to_seek "can't seek '%s'"
237 237
238enum label_type { 238enum label_type {
239 LABEL_DOS, LABEL_SUN, LABEL_SGI, LABEL_AIX, LABEL_OSF, LABEL_GPT 239 LABEL_DOS, LABEL_SUN, LABEL_SGI, LABEL_AIX, LABEL_OSF, LABEL_GPT
diff --git a/util-linux/flock.c b/util-linux/flock.c
index 12c16013c..1ed752a80 100644
--- a/util-linux/flock.c
+++ b/util-linux/flock.c
@@ -14,7 +14,7 @@
14//kbuild:lib-$(CONFIG_FLOCK) += flock.o 14//kbuild:lib-$(CONFIG_FLOCK) += flock.o
15 15
16//usage:#define flock_trivial_usage 16//usage:#define flock_trivial_usage
17//usage: "[-sxun] FD|{FILE [-c] PROG ARGS}" 17//usage: "[-sxun] FD | { FILE [-c] PROG ARGS }"
18//usage:#define flock_full_usage "\n\n" 18//usage:#define flock_full_usage "\n\n"
19//usage: "[Un]lock file descriptor, or lock FILE, run PROG\n" 19//usage: "[Un]lock file descriptor, or lock FILE, run PROG\n"
20//usage: "\n -s Shared lock" 20//usage: "\n -s Shared lock"
diff --git a/util-linux/hexdump_xxd.c b/util-linux/hexdump_xxd.c
index 29bbc6633..fe78f6242 100644
--- a/util-linux/hexdump_xxd.c
+++ b/util-linux/hexdump_xxd.c
@@ -41,15 +41,17 @@
41// -u use upper case hex letters. 41// -u use upper case hex letters.
42 42
43//usage:#define xxd_trivial_usage 43//usage:#define xxd_trivial_usage
44//usage: "[-pr] [-g N] [-c N] [-n LEN] [-s OFS] [FILE]" 44//usage: "[-pri] [-g N] [-c N] [-n LEN] [-s OFS] [-o OFS] [FILE]"
45//usage:#define xxd_full_usage "\n\n" 45//usage:#define xxd_full_usage "\n\n"
46//usage: "Hex dump FILE (or stdin)\n" 46//usage: "Hex dump FILE (or stdin)\n"
47//usage: "\n -g N Bytes per group" 47//usage: "\n -g N Bytes per group"
48//usage: "\n -c N Bytes per line" 48//usage: "\n -c N Bytes per line"
49//usage: "\n -p Show only hex bytes, assumes -c30" 49//usage: "\n -p Show only hex bytes, assumes -c30"
50//usage: "\n -i C include file style"
50// exactly the same help text lines in hexdump and xxd: 51// exactly the same help text lines in hexdump and xxd:
51//usage: "\n -l LENGTH Show only first LENGTH bytes" 52//usage: "\n -l LENGTH Show only first LENGTH bytes"
52//usage: "\n -s OFFSET Skip OFFSET bytes" 53//usage: "\n -s OFFSET Skip OFFSET bytes"
54//usage: "\n -o OFFSET Add OFFSET to displayed offset"
53//usage: "\n -r Reverse (with -p, assumes no offsets in input)" 55//usage: "\n -r Reverse (with -p, assumes no offsets in input)"
54 56
55#include "libbb.h" 57#include "libbb.h"
@@ -61,7 +63,11 @@
61#define OPT_s (1 << 1) 63#define OPT_s (1 << 1)
62#define OPT_a (1 << 2) 64#define OPT_a (1 << 2)
63#define OPT_p (1 << 3) 65#define OPT_p (1 << 3)
64#define OPT_r (1 << 4) 66#define OPT_i (1 << 4)
67#define OPT_r (1 << 5)
68#define OPT_g (1 << 6)
69#define OPT_c (1 << 7)
70#define OPT_o (1 << 8)
65 71
66static void reverse(unsigned opt, unsigned cols, const char *filename) 72static void reverse(unsigned opt, unsigned cols, const char *filename)
67{ 73{
@@ -122,20 +128,30 @@ static void reverse(unsigned opt, unsigned cols, const char *filename)
122 fflush_stdout_and_exit(EXIT_SUCCESS); 128 fflush_stdout_and_exit(EXIT_SUCCESS);
123} 129}
124 130
131static void print_C_style(const char *p, const char *hdr)
132{
133 printf(hdr, isdigit(p[0]) ? "__" : "");
134 while (*p) {
135 bb_putchar(isalnum(*p) ? *p : '_');
136 p++;
137 }
138}
139
125int xxd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 140int xxd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
126int xxd_main(int argc UNUSED_PARAM, char **argv) 141int xxd_main(int argc UNUSED_PARAM, char **argv)
127{ 142{
128 char buf[80]; 143 char buf[80];
129 dumper_t *dumper; 144 dumper_t *dumper;
130 char *opt_l, *opt_s; 145 char *opt_l, *opt_s, *opt_o;
131 unsigned bytes = 2; 146 unsigned bytes = 2;
132 unsigned cols = 0; 147 unsigned cols = 0;
133 unsigned opt; 148 unsigned opt;
149 int r;
134 150
135 dumper = alloc_dumper(); 151 dumper = alloc_dumper();
136 152
137 opt = getopt32(argv, "^" "l:s:aprg:+c:+" "\0" "?1" /* 1 argument max */, 153 opt = getopt32(argv, "^" "l:s:apirg:+c:+o:" "\0" "?1" /* 1 argument max */,
138 &opt_l, &opt_s, &bytes, &cols 154 &opt_l, &opt_s, &bytes, &cols, &opt_o
139 ); 155 );
140 argv += optind; 156 argv += optind;
141 157
@@ -158,14 +174,24 @@ int xxd_main(int argc UNUSED_PARAM, char **argv)
158 //BUGGY for /proc/version (unseekable?) 174 //BUGGY for /proc/version (unseekable?)
159 } 175 }
160 176
177 if (opt & OPT_o) {
178 /* -o accepts negative numbers too */
179 dumper->xxd_displayoff = xstrtoll(opt_o, /*base:*/ 0);
180 }
181
161 if (opt & OPT_p) { 182 if (opt & OPT_p) {
162 if (cols == 0) 183 if (cols == 0)
163 cols = 30; 184 cols = 30;
164 bytes = cols; /* -p ignores -gN */ 185 bytes = cols; /* -p ignores -gN */
165 } else { 186 } else {
166 if (cols == 0) 187 if (cols == 0)
167 cols = 16; 188 cols = (opt & OPT_i) ? 12 : 16;
168 bb_dump_add(dumper, "\"%08.8_ax: \""); // "address: " 189 if (opt & OPT_i) {
190 bytes = 1; // -i ignores -gN
191 // output is " 0xXX, 0xXX, 0xXX...", add leading space
192 bb_dump_add(dumper, "\" \"");
193 } else
194 bb_dump_add(dumper, "\"%08.8_ax: \""); // "address: "
169 } 195 }
170 196
171 if (opt & OPT_r) { 197 if (opt & OPT_r) {
@@ -173,11 +199,15 @@ int xxd_main(int argc UNUSED_PARAM, char **argv)
173 } 199 }
174 200
175 if (bytes < 1 || bytes >= cols) { 201 if (bytes < 1 || bytes >= cols) {
176 sprintf(buf, "%u/1 \"%%02x\"", cols); // cols * "xx" 202 sprintf(buf, "%u/1 \"%%02x\"", cols); // cols * "XX"
177 bb_dump_add(dumper, buf); 203 bb_dump_add(dumper, buf);
178 } 204 }
179 else if (bytes == 1) { 205 else if (bytes == 1) {
180 sprintf(buf, "%u/1 \"%%02x \"", cols); // cols * "xx " 206 if (opt & OPT_i)
207 sprintf(buf, "%u/1 \" 0x%%02x,\"", cols); // cols * " 0xXX,"
208//TODO: compat: omit the last comma after the very last byte
209 else
210 sprintf(buf, "%u/1 \"%%02x \"", cols); // cols * "XX "
181 bb_dump_add(dumper, buf); 211 bb_dump_add(dumper, buf);
182 } 212 }
183 else { 213 else {
@@ -201,13 +231,22 @@ int xxd_main(int argc UNUSED_PARAM, char **argv)
201 free(bigbuf); 231 free(bigbuf);
202 } 232 }
203 233
204 if (!(opt & OPT_p)) { 234 if (!(opt & (OPT_p|OPT_i))) {
205 sprintf(buf, "\" \"%u/1 \"%%_p\"\"\n\"", cols); // " ASCII\n" 235 sprintf(buf, "\" \"%u/1 \"%%_p\"\"\n\"", cols); // " ASCII\n"
206 bb_dump_add(dumper, buf); 236 bb_dump_add(dumper, buf);
207 } else { 237 } else {
208 bb_dump_add(dumper, "\"\n\""); 238 bb_dump_add(dumper, "\"\n\"");
209 dumper->eofstring = "\n"; 239 dumper->xxd_eofstring = "\n";
210 } 240 }
211 241
212 return bb_dump_dump(dumper, argv); 242 if ((opt & OPT_i) && argv[0]) {
243 print_C_style(argv[0], "unsigned char %s");
244 printf("[] = {\n");
245 }
246 r = bb_dump_dump(dumper, argv);
247 if (r == 0 && (opt & OPT_i) && argv[0]) {
248 print_C_style(argv[0], "};\nunsigned int %s");
249 printf("_len = %"OFF_FMT"u;\n", dumper->address);
250 }
251 return r;
213} 252}
diff --git a/util-linux/ionice.c b/util-linux/ionice.c
index c8fb1a777..82bd309d1 100644
--- a/util-linux/ionice.c
+++ b/util-linux/ionice.c
@@ -18,11 +18,13 @@
18//kbuild:lib-$(CONFIG_IONICE) += ionice.o 18//kbuild:lib-$(CONFIG_IONICE) += ionice.o
19 19
20//usage:#define ionice_trivial_usage 20//usage:#define ionice_trivial_usage
21//usage: "[-c 1-3] [-n 0-7] [-p PID] [PROG ARGS]" 21//usage: "[-c 1-3] [-n 0-7] [-t] { -p PID | PROG ARGS }"
22//TODO: | -P PGID | -u UID; also -pPu can take _list of_ IDs
22//usage:#define ionice_full_usage "\n\n" 23//usage:#define ionice_full_usage "\n\n"
23//usage: "Change I/O priority and class\n" 24//usage: "Change I/O priority and class\n"
24//usage: "\n -c N Class. 1:realtime 2:best-effort 3:idle" 25//usage: "\n -c N Class. 1:realtime 2:best-effort 3:idle"
25//usage: "\n -n N Priority" 26//usage: "\n -n N Priority"
27//usage: "\n -t Ignore errors"
26 28
27#include <sys/syscall.h> 29#include <sys/syscall.h>
28#include <asm/unistd.h> 30#include <asm/unistd.h>
@@ -64,14 +66,15 @@ int ionice_main(int argc UNUSED_PARAM, char **argv)
64 int pid = 0; /* affect own process */ 66 int pid = 0; /* affect own process */
65 int opt; 67 int opt;
66 enum { 68 enum {
67 OPT_n = 1, 69 OPT_n = 1 << 0,
68 OPT_c = 2, 70 OPT_c = 1 << 1,
69 OPT_p = 4, 71 OPT_p = 1 << 2,
72 OPT_t = 1 << 3,
70 }; 73 };
71 74
72 /* Numeric params */
73 /* '+': stop at first non-option */ 75 /* '+': stop at first non-option */
74 opt = getopt32(argv, "+n:+c:+p:+", &pri, &ioclass, &pid); 76 /* numeric params for -n -c -p */
77 opt = getopt32(argv, "+""n:+c:+p:+t", &pri, &ioclass, &pid);
75 argv += optind; 78 argv += optind;
76 79
77 if (opt & OPT_c) { 80 if (opt & OPT_c) {
@@ -104,7 +107,8 @@ int ionice_main(int argc UNUSED_PARAM, char **argv)
104//pri, ioclass, pri | (ioclass << IOPRIO_CLASS_SHIFT)); 107//pri, ioclass, pri | (ioclass << IOPRIO_CLASS_SHIFT));
105 pri |= (ioclass << IOPRIO_CLASS_SHIFT); 108 pri |= (ioclass << IOPRIO_CLASS_SHIFT);
106 if (ioprio_set(IOPRIO_WHO_PROCESS, pid, pri) == -1) 109 if (ioprio_set(IOPRIO_WHO_PROCESS, pid, pri) == -1)
107 bb_perror_msg_and_die("ioprio_%cet", 's'); 110 if (!(opt & OPT_t))
111 bb_perror_msg_and_die("ioprio_%cet", 's');
108 if (argv[0]) { 112 if (argv[0]) {
109 BB_EXECVP_or_die(argv); 113 BB_EXECVP_or_die(argv);
110 } 114 }
diff --git a/util-linux/mountpoint.c b/util-linux/mountpoint.c
index a44cf6013..28b1e7a54 100644
--- a/util-linux/mountpoint.c
+++ b/util-linux/mountpoint.c
@@ -19,13 +19,14 @@
19//kbuild:lib-$(CONFIG_MOUNTPOINT) += mountpoint.o 19//kbuild:lib-$(CONFIG_MOUNTPOINT) += mountpoint.o
20 20
21//usage:#define mountpoint_trivial_usage 21//usage:#define mountpoint_trivial_usage
22//usage: "[-q] <[-dn] DIR | -x DEVICE>" 22//usage: "[-q] { [-dn] DIR | -x DEVICE }"
23//usage:#define mountpoint_full_usage "\n\n" 23//usage:#define mountpoint_full_usage "\n\n"
24//usage: "Check if the directory is a mountpoint\n" 24//usage: "Check if DIR is a mountpoint\n"
25//usage: "\n -q Quiet" 25//usage: "\n -q Quiet"
26//usage: "\n -d Print major/minor device number of the filesystem" 26//usage: "\n -d Print major:minor of the filesystem"
27//usage: "\n -n Print device name of the filesystem" 27//usage: "\n -n Print device name of the filesystem"
28//usage: "\n -x Print major/minor device number of the blockdevice" 28//////// -n is not supported by util-linux-2.36.1 ^^^^^^^^^^^^^^^^^^
29//usage: "\n -x Print major:minor of DEVICE"
29//usage: 30//usage:
30//usage:#define mountpoint_example_usage 31//usage:#define mountpoint_example_usage
31//usage: "$ mountpoint /proc\n" 32//usage: "$ mountpoint /proc\n"
diff --git a/util-linux/readprofile.c b/util-linux/readprofile.c
index 32d9987e7..f11c62292 100644
--- a/util-linux/readprofile.c
+++ b/util-linux/readprofile.c
@@ -44,8 +44,8 @@
44//usage:#define readprofile_trivial_usage 44//usage:#define readprofile_trivial_usage
45//usage: "[OPTIONS]" 45//usage: "[OPTIONS]"
46//usage:#define readprofile_full_usage "\n\n" 46//usage:#define readprofile_full_usage "\n\n"
47//usage: " -m mapfile (Default: /boot/System.map)" 47//usage: " -m MAPFILE (Default: /boot/System.map)"
48//usage: "\n -p profile (Default: /proc/profile)" 48//usage: "\n -p PROFILE (Default: /proc/profile)"
49//usage: "\n -M NUM Set the profiling multiplier to NUM" 49//usage: "\n -M NUM Set the profiling multiplier to NUM"
50//usage: "\n -i Print only info about the sampling step" 50//usage: "\n -i Print only info about the sampling step"
51//usage: "\n -v Verbose" 51//usage: "\n -v Verbose"
diff --git a/util-linux/renice.c b/util-linux/renice.c
index a318ffce0..fc72550f4 100644
--- a/util-linux/renice.c
+++ b/util-linux/renice.c
@@ -29,7 +29,7 @@
29//kbuild:lib-$(CONFIG_RENICE) += renice.o 29//kbuild:lib-$(CONFIG_RENICE) += renice.o
30 30
31//usage:#define renice_trivial_usage 31//usage:#define renice_trivial_usage
32//usage: "[-n] PRIORITY [[-p | -g | -u] ID...]..." 32//usage: "[-n] PRIORITY [[-p|g|u] ID...]..."
33//usage:#define renice_full_usage "\n\n" 33//usage:#define renice_full_usage "\n\n"
34//usage: "Change scheduling priority of a running process\n" 34//usage: "Change scheduling priority of a running process\n"
35//usage: "\n -n Add PRIORITY to current nice value" 35//usage: "\n -n Add PRIORITY to current nice value"
diff --git a/util-linux/switch_root.c b/util-linux/switch_root.c
index f2674b5ac..901c0b8db 100644
--- a/util-linux/switch_root.c
+++ b/util-linux/switch_root.c
@@ -68,11 +68,22 @@ extern int capget(cap_user_header_t header, const cap_user_data_t data);
68# define MS_MOVE 8192 68# define MS_MOVE 8192
69#endif 69#endif
70 70
71static void delete_contents(const char *directory, dev_t rootdev);
72
73static int FAST_FUNC rmrf(const char *directory, struct dirent *d, void *rootdevp)
74{
75 char *newdir = concat_subpath_file(directory, d->d_name);
76 if (newdir) { // not . or ..
77 // Recurse to delete contents
78 delete_contents(newdir, *(dev_t*)rootdevp);
79 free(newdir);
80 }
81 return 0;
82}
83
71// Recursively delete contents of rootfs 84// Recursively delete contents of rootfs
72static void delete_contents(const char *directory, dev_t rootdev) 85static void delete_contents(const char *directory, dev_t rootdev)
73{ 86{
74 DIR *dir;
75 struct dirent *d;
76 struct stat st; 87 struct stat st;
77 88
78 // Don't descend into other filesystems 89 // Don't descend into other filesystems
@@ -81,25 +92,7 @@ static void delete_contents(const char *directory, dev_t rootdev)
81 92
82 // Recursively delete the contents of directories 93 // Recursively delete the contents of directories
83 if (S_ISDIR(st.st_mode)) { 94 if (S_ISDIR(st.st_mode)) {
84 dir = opendir(directory); 95 iterate_on_dir(directory, rmrf, &rootdev);
85 if (dir) {
86 while ((d = readdir(dir))) {
87 char *newdir = d->d_name;
88
89 // Skip . and ..
90 if (DOT_OR_DOTDOT(newdir))
91 continue;
92
93 // Recurse to delete contents
94 newdir = concat_path_file(directory, newdir);
95 delete_contents(newdir, rootdev);
96 free(newdir);
97 }
98 closedir(dir);
99
100 // Directory should now be empty, zap it
101 rmdir(directory);
102 }
103 } else { 96 } else {
104 // It wasn't a directory, zap it 97 // It wasn't a directory, zap it
105 unlink(directory); 98 unlink(directory);
diff --git a/util-linux/taskset.c b/util-linux/taskset.c
index b542f8c83..afe2f04d2 100644
--- a/util-linux/taskset.c
+++ b/util-linux/taskset.c
@@ -34,10 +34,12 @@
34//kbuild:lib-$(CONFIG_TASKSET) += taskset.o 34//kbuild:lib-$(CONFIG_TASKSET) += taskset.o
35 35
36//usage:#define taskset_trivial_usage 36//usage:#define taskset_trivial_usage
37//usage: "[-p] [HEXMASK] PID | PROG ARGS" 37//usage: "[-ap] [HEXMASK"IF_FEATURE_TASKSET_CPULIST(" | -c LIST")"] { PID | PROG ARGS }"
38//usage:#define taskset_full_usage "\n\n" 38//usage:#define taskset_full_usage "\n\n"
39//usage: "Set or get CPU affinity\n" 39//usage: "Set or get CPU affinity\n"
40//usage: "\n -p Operate on an existing PID" 40//usage: "\n -p Operate on PID"
41//usage: "\n -a Operate on all threads"
42//usage: "\n -c Affinity is a list, not mask"
41//usage: 43//usage:
42//usage:#define taskset_example_usage 44//usage:#define taskset_example_usage
43//usage: "$ taskset 0x7 ./dgemm_test&\n" 45//usage: "$ taskset 0x7 ./dgemm_test&\n"
@@ -205,42 +207,18 @@ static void print_cpulist(const ul *mask, unsigned mask_size_in_bytes)
205} 207}
206#endif 208#endif
207 209
208int taskset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 210enum {
209int taskset_main(int argc UNUSED_PARAM, char **argv) 211 OPT_p = 1 << 0,
212 OPT_a = 1 << 1,
213 OPT_c = (1 << 2) * ENABLE_FEATURE_TASKSET_CPULIST,
214};
215
216static int process_pid_str(const char *pid_str, unsigned opts, char *aff)
210{ 217{
211 ul *mask; 218 ul *mask;
212 unsigned mask_size_in_bytes; 219 unsigned mask_size_in_bytes;
213 pid_t pid = 0;
214 const char *current_new; 220 const char *current_new;
215 char *aff; 221 pid_t pid = xatoi_positive(pid_str);
216 unsigned opts;
217 enum {
218 OPT_p = 1 << 0,
219 OPT_c = (1 << 1) * ENABLE_FEATURE_TASKSET_CPULIST,
220 };
221
222 /* NB: we mimic util-linux's taskset: -p does not take
223 * an argument, i.e., "-pN" is NOT valid, only "-p N"!
224 * Indeed, util-linux-2.13-pre7 uses:
225 * getopt_long(argc, argv, "+pchV", ...), not "...p:..." */
226
227 opts = getopt32(argv, "^+" "p"IF_FEATURE_TASKSET_CPULIST("c")
228 "\0" "-1" /* at least 1 arg */);
229 argv += optind;
230
231 aff = *argv++;
232 if (opts & OPT_p) {
233 char *pid_str = aff;
234 if (*argv) { /* "-p <aff> <pid> ...rest.is.ignored..." */
235 pid_str = *argv; /* NB: *argv != NULL in this case */
236 }
237 /* else it was just "-p <pid>", and *argv == NULL */
238 pid = xatoul_range(pid_str, 1, ((unsigned)(pid_t)ULONG_MAX) >> 1);
239 } else {
240 /* <aff> <cmd...> */
241 if (!*argv)
242 bb_show_usage();
243 }
244 222
245 mask_size_in_bytes = SZOF_UL; 223 mask_size_in_bytes = SZOF_UL;
246 current_new = "current"; 224 current_new = "current";
@@ -255,13 +233,12 @@ int taskset_main(int argc UNUSED_PARAM, char **argv)
255#endif 233#endif
256 printf("pid %d's %s affinity mask: "TASKSET_PRINTF_MASK"\n", 234 printf("pid %d's %s affinity mask: "TASKSET_PRINTF_MASK"\n",
257 pid, current_new, from_mask(mask, mask_size_in_bytes)); 235 pid, current_new, from_mask(mask, mask_size_in_bytes));
258 if (*argv == NULL) { 236 if (!aff) {
259 /* Either it was just "-p <pid>", 237 /* Either it was just "-p <pid>",
260 * or it was "-p <aff> <pid>" and we came here 238 * or it was "-p <aff> <pid>" and we came here
261 * for the second time (see goto below) */ 239 * for the second time (see goto below) */
262 return EXIT_SUCCESS; 240 return 0;
263 } 241 }
264 *argv = NULL;
265 current_new = "new"; 242 current_new = "new";
266 } 243 }
267 memset(mask, 0, mask_size_in_bytes); 244 memset(mask, 0, mask_size_in_bytes);
@@ -331,8 +308,61 @@ int taskset_main(int argc UNUSED_PARAM, char **argv)
331 bb_perror_msg_and_die("can't %cet pid %d's affinity", 's', pid); 308 bb_perror_msg_and_die("can't %cet pid %d's affinity", 's', pid);
332 //bb_error_msg("set mask[0]:%lx", mask[0]); 309 //bb_error_msg("set mask[0]:%lx", mask[0]);
333 310
334 if (!argv[0]) /* "-p <aff> <pid> [...ignored...]" */ 311 if ((opts & OPT_p) && aff) { /* "-p <aff> <pid> [...ignored...]" */
312 aff = NULL;
335 goto print_aff; /* print new affinity and exit */ 313 goto print_aff; /* print new affinity and exit */
314 }
315 return 0;
316}
317
318static int FAST_FUNC iter(const char *dn UNUSED_PARAM, struct dirent *ent, void *aff)
319{
320 if (isdigit(ent->d_name[0]))
321 return process_pid_str(ent->d_name, option_mask32, aff);
322 return 0;
323}
324
325int taskset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
326int taskset_main(int argc UNUSED_PARAM, char **argv)
327{
328 const char *pid_str;
329 char *aff;
330 unsigned opts;
336 331
337 BB_EXECVP_or_die(argv); 332 /* NB: we mimic util-linux's taskset: -p does not take
333 * an argument, i.e., "-pN" is NOT valid, only "-p N"!
334 * Indeed, util-linux-2.13-pre7 uses:
335 * getopt_long(argc, argv, "+pchV", ...), not "...p:..." */
336
337 opts = getopt32(argv, "^+" "pa"IF_FEATURE_TASKSET_CPULIST("c")
338 "\0" "-1" /* at least 1 arg */);
339 argv += optind;
340
341 aff = *argv++;
342 if (!(opts & OPT_p)) {
343 /* <aff> <cmd...> */
344 if (!*argv)
345 bb_show_usage();
346 process_pid_str("0", opts, aff);
347 BB_EXECVP_or_die(argv);
348 }
349
350 pid_str = aff;
351 if (*argv) /* "-p <aff> <pid> ...rest.is.ignored..." */
352 pid_str = *argv;
353 else
354 aff = NULL;
355
356 if (opts & OPT_a) {
357 char *dn;
358 int r;
359
360 dn = xasprintf("/proc/%s/task", pid_str);
361 r = iterate_on_dir(dn, iter, aff);
362 IF_FEATURE_CLEAN_UP(free(dn);)
363 if (r == 0)
364 return r; /* EXIT_SUCCESS */
365 /* else: no /proc/PID/task, act as if no -a was given */
366 }
367 return process_pid_str(pid_str, opts, aff);
338} 368}