aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2018-09-10 14:37:07 +0100
committerRon Yorston <rmy@pobox.com>2018-09-10 14:59:33 +0100
commitd89ced75b204f0eb5611f522864beb81d1b393f5 (patch)
tree5daa31427e287fe079a0ef551097753773fdb266
parentf72845d9332fa6311a46dbcad3180d5008182982 (diff)
parent05b18065ab9c375f6185b65a3631d4c6cc1a4be9 (diff)
downloadbusybox-w32-d89ced75b204f0eb5611f522864beb81d1b393f5.tar.gz
busybox-w32-d89ced75b204f0eb5611f522864beb81d1b393f5.tar.bz2
busybox-w32-d89ced75b204f0eb5611f522864beb81d1b393f5.zip
Merge branch 'busybox' into merge
-rwxr-xr-xapplets/install.sh2
-rw-r--r--archival/tar.c80
-rw-r--r--coreutils/cp.c2
-rw-r--r--coreutils/ls.c10
-rw-r--r--coreutils/sleep.c78
-rw-r--r--coreutils/timeout.c30
-rwxr-xr-xexamples/var_service/ntpd/ntp.script4
-rw-r--r--include/libbb.h8
-rw-r--r--libbb/copy_file.c3
-rw-r--r--libbb/duration.c78
-rw-r--r--libbb/get_line_from_file.c4
-rw-r--r--libbb/remove_file.c2
-rw-r--r--libbb/unicode.c2
-rw-r--r--loginutils/chpasswd.c18
-rw-r--r--mailutils/mail.c29
-rw-r--r--mailutils/mail.h4
-rw-r--r--mailutils/makemime.c2
-rw-r--r--mailutils/sendmail.c58
-rw-r--r--networking/ip.c6
-rw-r--r--networking/nslookup.c104
-rw-r--r--networking/ntpd.c37
-rw-r--r--networking/ping.c28
-rw-r--r--networking/udhcp/dhcpc.c16
-rw-r--r--networking/whois.c14
-rw-r--r--procps/smemcap.c6
-rw-r--r--procps/top.c14
-rw-r--r--procps/uptime.c30
-rw-r--r--selinux/chcon.c8
-rw-r--r--shell/ash.c127
-rw-r--r--shell/ash_test/ash-glob/glob_bkslash_in_var.right4
-rwxr-xr-xshell/ash_test/ash-glob/glob_bkslash_in_var.tests10
-rw-r--r--shell/ash_test/ash-misc/empty_for1.right1
-rwxr-xr-xshell/ash_test/ash-misc/empty_for1.tests5
-rw-r--r--shell/ash_test/ash-misc/env_and_func.right2
-rwxr-xr-xshell/ash_test/ash-misc/env_and_func.tests2
-rw-r--r--shell/ash_test/ash-vars/var_bash_pattern_starting_with_slash.right2
-rwxr-xr-xshell/ash_test/ash-vars/var_bash_pattern_starting_with_slash.tests3
-rw-r--r--shell/ash_test/ash-vars/var_leak.right2
-rwxr-xr-xshell/ash_test/ash-vars/var_leak.tests4
-rw-r--r--shell/ash_test/ash-vars/var_wordsplit_ifs5.right1
-rwxr-xr-xshell/ash_test/ash-vars/var_wordsplit_ifs5.tests4
-rw-r--r--shell/hush.c340
-rw-r--r--shell/hush_test/hush-glob/glob_bkslash_in_var.right4
-rwxr-xr-xshell/hush_test/hush-glob/glob_bkslash_in_var.tests10
-rw-r--r--shell/hush_test/hush-misc/empty_for1.right1
-rwxr-xr-xshell/hush_test/hush-misc/empty_for1.tests5
-rwxr-xr-xshell/hush_test/hush-misc/env_and_func.tests2
-rw-r--r--shell/hush_test/hush-quoting/quote_in_varexp1.right2
-rwxr-xr-xshell/hush_test/hush-quoting/quote_in_varexp1.tests2
-rw-r--r--shell/hush_test/hush-vars/var_bash_pattern_starting_with_slash.right2
-rwxr-xr-xshell/hush_test/hush-vars/var_bash_pattern_starting_with_slash.tests3
-rw-r--r--shell/hush_test/hush-vars/var_wordsplit_ifs5.right1
-rwxr-xr-xshell/hush_test/hush-vars/var_wordsplit_ifs5.tests4
-rw-r--r--shell/shell_common.c57
-rw-r--r--shell/shell_common.h22
-rw-r--r--sysklogd/klogd.c15
-rw-r--r--sysklogd/syslogd.c25
-rw-r--r--util-linux/fdisk.c131
-rw-r--r--util-linux/fdisk_sgi.c2
-rw-r--r--util-linux/fdisk_sun.c2
60 files changed, 978 insertions, 496 deletions
diff --git a/applets/install.sh b/applets/install.sh
index 9aede0f53..415896893 100755
--- a/applets/install.sh
+++ b/applets/install.sh
@@ -83,7 +83,7 @@ install -m 755 busybox "$prefix/bin/busybox" || exit 1
83for i in $h; do 83for i in $h; do
84 appdir=`dirname "$i"` 84 appdir=`dirname "$i"`
85 app=`basename "$i"` 85 app=`basename "$i"`
86 if [ x"$noclobber" = x"1" ] && [ -e "$prefix/$i" ]; then 86 if [ x"$noclobber" = x"1" ] && ([ -e "$prefix/$i" ] || [ -h "$prefix/$i" ]); then
87 echo " $prefix/$i already exists" 87 echo " $prefix/$i already exists"
88 continue 88 continue
89 fi 89 fi
diff --git a/archival/tar.c b/archival/tar.c
index e1af27401..9a171ceea 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -601,35 +601,46 @@ static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statb
601/* Don't inline: vfork scares gcc and pessimizes code */ 601/* Don't inline: vfork scares gcc and pessimizes code */
602static void NOINLINE vfork_compressor(int tar_fd, const char *gzip) 602static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
603{ 603{
604 pid_t gzipPid;
605
606 // On Linux, vfork never unpauses parent early, although standard 604 // On Linux, vfork never unpauses parent early, although standard
607 // allows for that. Do we want to waste bytes checking for it? 605 // allows for that. Do we want to waste bytes checking for it?
608# define WAIT_FOR_CHILD 0 606# define WAIT_FOR_CHILD 0
609 volatile int vfork_exec_errno = 0; 607 volatile int vfork_exec_errno = 0;
610 struct fd_pair gzipDataPipe; 608 struct fd_pair data;
611# if WAIT_FOR_CHILD 609# if WAIT_FOR_CHILD
612 struct fd_pair gzipStatusPipe; 610 struct fd_pair status;
613 xpiped_pair(gzipStatusPipe); 611 xpiped_pair(status);
614# endif 612# endif
615 xpiped_pair(gzipDataPipe); 613 xpiped_pair(data);
616 614
617 signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */ 615 signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */
618 616
619 gzipPid = xvfork(); 617 if (xvfork() == 0) {
620
621 if (gzipPid == 0) {
622 /* child */ 618 /* child */
619 int tfd;
623 /* NB: close _first_, then move fds! */ 620 /* NB: close _first_, then move fds! */
624 close(gzipDataPipe.wr); 621 close(data.wr);
625# if WAIT_FOR_CHILD 622# if WAIT_FOR_CHILD
626 close(gzipStatusPipe.rd); 623 close(status.rd);
627 /* gzipStatusPipe.wr will close only on exec - 624 /* status.wr will close only on exec -
628 * parent waits for this close to happen */ 625 * parent waits for this close to happen */
629 fcntl(gzipStatusPipe.wr, F_SETFD, FD_CLOEXEC); 626 fcntl(status.wr, F_SETFD, FD_CLOEXEC);
630# endif 627# endif
631 xmove_fd(gzipDataPipe.rd, 0); 628 /* copy it: parent's tar_fd variable must not change */
632 xmove_fd(tar_fd, 1); 629 tfd = tar_fd;
630 if (tfd == 0) {
631 /* Output tar fd may be zero.
632 * xmove_fd(data.rd, 0) would destroy it.
633 * Reproducer:
634 * exec 0>&-
635 * exec 1>&-
636 * tar czf Z.tar.gz FILE
637 * Swapping move_fd's order wouldn't work:
638 * data.rd is 1 and _it_ would be destroyed.
639 */
640 tfd = dup(tfd);
641 }
642 xmove_fd(data.rd, 0);
643 xmove_fd(tfd, 1);
633 /* exec gzip/bzip2 program/applet */ 644 /* exec gzip/bzip2 program/applet */
634 BB_EXECLP(gzip, gzip, "-f", (char *)0); 645 BB_EXECLP(gzip, gzip, "-f", (char *)0);
635 vfork_exec_errno = errno; 646 vfork_exec_errno = errno;
@@ -637,20 +648,18 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
637 } 648 }
638 649
639 /* parent */ 650 /* parent */
640 xmove_fd(gzipDataPipe.wr, tar_fd); 651 xmove_fd(data.wr, tar_fd);
641 close(gzipDataPipe.rd); 652 close(data.rd);
642# if WAIT_FOR_CHILD 653# if WAIT_FOR_CHILD
643 close(gzipStatusPipe.wr); 654 close(status.wr);
644 while (1) { 655 while (1) {
645 char buf;
646 int n;
647
648 /* Wait until child execs (or fails to) */ 656 /* Wait until child execs (or fails to) */
649 n = full_read(gzipStatusPipe.rd, &buf, 1); 657 char buf;
658 int n = full_read(status.rd, &buf, 1);
650 if (n < 0 /* && errno == EAGAIN */) 659 if (n < 0 /* && errno == EAGAIN */)
651 continue; /* try it again */ 660 continue; /* try it again */
652 } 661 }
653 close(gzipStatusPipe.rd); 662 close(status.rd);
654# endif 663# endif
655 if (vfork_exec_errno) { 664 if (vfork_exec_errno) {
656 errno = vfork_exec_errno; 665 errno = vfork_exec_errno;
@@ -754,11 +763,7 @@ static NOINLINE int writeTarFile(
754 return errorFlag; 763 return errorFlag;
755} 764}
756 765
757#else /* !FEATURE_TAR_CREATE */ 766#endif /* FEATURE_TAR_CREATE */
758
759# define writeTarFile(...) 0
760
761#endif
762 767
763#if ENABLE_FEATURE_TAR_FROM 768#if ENABLE_FEATURE_TAR_FROM
764static llist_t *append_file_list_to_list(llist_t *list) 769static llist_t *append_file_list_to_list(llist_t *list)
@@ -1190,17 +1195,16 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
1190 if (LONE_DASH(tar_filename)) { 1195 if (LONE_DASH(tar_filename)) {
1191 tar_handle->src_fd = tar_fd; 1196 tar_handle->src_fd = tar_fd;
1192 tar_handle->seek = seek_by_read; 1197 tar_handle->seek = seek_by_read;
1198 } else
1199 if (ENABLE_FEATURE_TAR_AUTODETECT
1200 && flags == O_RDONLY
1201 && !(opt & OPT_ANY_COMPRESS)
1202 ) {
1203 tar_handle->src_fd = open_zipped(tar_filename, /*fail_if_not_compressed:*/ 0);
1204 if (tar_handle->src_fd < 0)
1205 bb_perror_msg_and_die("can't open '%s'", tar_filename);
1193 } else { 1206 } else {
1194 if (ENABLE_FEATURE_TAR_AUTODETECT 1207 tar_handle->src_fd = xopen(tar_filename, flags);
1195 && flags == O_RDONLY
1196 && !(opt & OPT_ANY_COMPRESS)
1197 ) {
1198 tar_handle->src_fd = open_zipped(tar_filename, /*fail_if_not_compressed:*/ 0);
1199 if (tar_handle->src_fd < 0)
1200 bb_perror_msg_and_die("can't open '%s'", tar_filename);
1201 } else {
1202 tar_handle->src_fd = xopen(tar_filename, flags);
1203 }
1204 } 1208 }
1205 } 1209 }
1206 1210
diff --git a/coreutils/cp.c b/coreutils/cp.c
index ae60623d3..b26c0e954 100644
--- a/coreutils/cp.c
+++ b/coreutils/cp.c
@@ -114,7 +114,7 @@ int cp_main(int argc, char **argv)
114# endif 114# endif
115 ); 115 );
116# if ENABLE_FEATURE_CP_REFLINK 116# if ENABLE_FEATURE_CP_REFLINK
117 BUILD_BUG_ON(OPT_reflink != FILEUTILS_REFLINK); 117 BUILD_BUG_ON((int)OPT_reflink != (int)FILEUTILS_REFLINK);
118 if (flags & FILEUTILS_REFLINK) { 118 if (flags & FILEUTILS_REFLINK) {
119 if (!reflink) 119 if (!reflink)
120 flags |= FILEUTILS_REFLINK_ALWAYS; 120 flags |= FILEUTILS_REFLINK_ALWAYS;
diff --git a/coreutils/ls.c b/coreutils/ls.c
index b0c543d28..9daa15470 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -1018,7 +1018,15 @@ static void scan_and_display_dirs_recur(struct dnode **dn, int first)
1018 subdnp = scan_one_dir((*dn)->fullname, &nfiles); 1018 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
1019#if ENABLE_DESKTOP 1019#if ENABLE_DESKTOP
1020 if (option_mask32 & (OPT_s|OPT_l)) { 1020 if (option_mask32 & (OPT_s|OPT_l)) {
1021 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp)); 1021 if (option_mask32 & OPT_h) {
1022 printf("total %-"HUMAN_READABLE_MAX_WIDTH_STR"s\n",
1023 /* print size, no fractions, use suffixes */
1024 make_human_readable_str(calculate_blocks(subdnp) * 1024,
1025 0, 0)
1026 );
1027 } else {
1028 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
1029 }
1022 } 1030 }
1023#endif 1031#endif
1024 if (nfiles > 0) { 1032 if (nfiles > 0) {
diff --git a/coreutils/sleep.c b/coreutils/sleep.c
index 9b9581ca9..126665839 100644
--- a/coreutils/sleep.c
+++ b/coreutils/sleep.c
@@ -32,13 +32,6 @@
32//config: depends on SLEEP 32//config: depends on SLEEP
33//config: help 33//config: help
34//config: Allow sleep to pause for specified minutes, hours, and days. 34//config: Allow sleep to pause for specified minutes, hours, and days.
35//config:
36//config:config FEATURE_FLOAT_SLEEP
37//config: bool "Enable fractional arguments"
38//config: default y
39//config: depends on FEATURE_FANCY_SLEEP
40//config: help
41//config: Allow for fractional numeric parameters.
42 35
43/* Do not make this applet NOFORK. It breaks ^C-ing of pauses in shells */ 36/* Do not make this applet NOFORK. It breaks ^C-ing of pauses in shells */
44//applet:IF_SLEEP(APPLET(sleep, BB_DIR_BIN, BB_SUID_DROP)) 37//applet:IF_SLEEP(APPLET(sleep, BB_DIR_BIN, BB_SUID_DROP))
@@ -66,89 +59,28 @@
66 59
67#include "libbb.h" 60#include "libbb.h"
68 61
69#if ENABLE_FEATURE_FANCY_SLEEP || ENABLE_FEATURE_FLOAT_SLEEP
70static const struct suffix_mult sfx[] = {
71 { "s", 1 },
72 { "m", 60 },
73 { "h", 60*60 },
74 { "d", 24*60*60 },
75 { "", 0 }
76};
77#endif
78
79int sleep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 62int sleep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
80int sleep_main(int argc UNUSED_PARAM, char **argv) 63int sleep_main(int argc UNUSED_PARAM, char **argv)
81{ 64{
82#if ENABLE_FEATURE_FLOAT_SLEEP 65 duration_t duration;
83 double duration;
84 struct timespec ts;
85#else
86 unsigned duration;
87#endif
88 66
89 ++argv; 67 ++argv;
90 if (!*argv) 68 if (!*argv)
91 bb_show_usage(); 69 bb_show_usage();
92 70
93#if ENABLE_FEATURE_FLOAT_SLEEP 71#if ENABLE_FEATURE_FANCY_SLEEP
94 72# if ENABLE_FLOAT_DURATION
95# if ENABLE_LOCALE_SUPPORT
96 /* undo busybox.c setlocale */ 73 /* undo busybox.c setlocale */
97 setlocale(LC_NUMERIC, "C"); 74 setlocale(LC_NUMERIC, "C");
98# endif 75# endif
99 duration = 0; 76 duration = 0;
100 do { 77 do {
101 char *arg = *argv; 78 duration += parse_duration_str(*argv);
102 if (strchr(arg, '.')) {
103 double d;
104 char *pp;
105 int len = strspn(arg, "0123456789.");
106 char sv = arg[len];
107 arg[len] = '\0';
108 errno = 0;
109 d = strtod(arg, &pp);
110 if (errno || *pp)
111 bb_show_usage();
112 arg += len;
113 *arg-- = sv;
114 sv = *arg;
115 *arg = '1';
116 duration += d * xatoul_sfx(arg, sfx);
117 *arg = sv;
118 } else {
119 duration += xatoul_sfx(arg, sfx);
120 }
121 } while (*++argv); 79 } while (*++argv);
122 80 sleep_for_duration(duration);
123 ts.tv_sec = MAXINT(typeof(ts.tv_sec));
124 ts.tv_nsec = 0;
125 if (duration >= 0 && duration < ts.tv_sec) {
126 ts.tv_sec = duration;
127 ts.tv_nsec = (duration - ts.tv_sec) * 1000000000;
128 }
129 do {
130 errno = 0;
131 nanosleep(&ts, &ts);
132 } while (errno == EINTR);
133
134#elif ENABLE_FEATURE_FANCY_SLEEP
135
136 duration = 0;
137 do {
138 duration += xatou_range_sfx(*argv, 0, UINT_MAX - duration, sfx);
139 } while (*++argv);
140 sleep(duration);
141
142#else /* simple */ 81#else /* simple */
143
144 duration = xatou(*argv); 82 duration = xatou(*argv);
145 sleep(duration); 83 sleep(duration);
146 // Off. If it's really needed, provide example why
147 //if (sleep(duration)) {
148 // bb_perror_nomsg_and_die();
149 //}
150
151#endif 84#endif
152
153 return EXIT_SUCCESS; 85 return EXIT_SUCCESS;
154} 86}
diff --git a/coreutils/timeout.c b/coreutils/timeout.c
index c1be7b198..3b2140807 100644
--- a/coreutils/timeout.c
+++ b/coreutils/timeout.c
@@ -39,10 +39,10 @@
39//kbuild:lib-$(CONFIG_TIMEOUT) += timeout.o 39//kbuild:lib-$(CONFIG_TIMEOUT) += timeout.o
40 40
41//usage:#define timeout_trivial_usage 41//usage:#define timeout_trivial_usage
42//usage: "[-t SECS] [-s SIG] PROG ARGS" 42//usage: "[-s SIG] SECS PROG ARGS"
43//usage:#define timeout_full_usage "\n\n" 43//usage:#define timeout_full_usage "\n\n"
44//usage: "Runs PROG. Sends SIG to it if it is not gone in SECS seconds.\n" 44//usage: "Runs PROG. Sends SIG to it if it is not gone in SECS seconds.\n"
45//usage: "Defaults: SECS: 10, SIG: TERM." 45//usage: "Default SIG: TERM."
46 46
47#include "libbb.h" 47#include "libbb.h"
48 48
@@ -68,7 +68,7 @@ int timeout_main(int argc UNUSED_PARAM, char **argv)
68 DWORD status = EXIT_SUCCESS; 68 DWORD status = EXIT_SUCCESS;
69#endif 69#endif
70 int parent = 0; 70 int parent = 0;
71 int timeout = 10; 71 int timeout;
72 pid_t pid; 72 pid_t pid;
73#if !BB_MMU 73#if !BB_MMU
74 char *sv1, *sv2; 74 char *sv1, *sv2;
@@ -83,25 +83,34 @@ int timeout_main(int argc UNUSED_PARAM, char **argv)
83 83
84 /* -t SECONDS; -p PARENT_PID */ 84 /* -t SECONDS; -p PARENT_PID */
85 /* '+': stop at first non-option */ 85 /* '+': stop at first non-option */
86 getopt32(argv, "+s:t:+" USE_FOR_NOMMU("p:+"), &opt_s, &timeout, &parent); 86 getopt32(argv, "+s:" USE_FOR_NOMMU("p:+"), &opt_s, &parent);
87 /*argv += optind; - no, wait for bb_daemonize_or_rexec! */ 87 /*argv += optind; - no, wait for bb_daemonize_or_rexec! */
88
88 signo = get_signum(opt_s); 89 signo = get_signum(opt_s);
89#if !ENABLE_PLATFORM_MINGW32 90#if !ENABLE_PLATFORM_MINGW32
90 if (signo < 0) 91 if (signo < 0)
92#else
93 if (signo != SIGTERM && signo != SIGKILL && signo != 0)
94#endif
91 bb_error_msg_and_die("unknown signal '%s'", opt_s); 95 bb_error_msg_and_die("unknown signal '%s'", opt_s);
92 96
97 if (!argv[optind])
98 bb_show_usage();
99 timeout = parse_duration_str(argv[optind++]);
100 if (!argv[optind]) /* no PROG? */
101 bb_show_usage();
102
103#if !ENABLE_PLATFORM_MINGW32
93 /* We want to create a grandchild which will watch 104 /* We want to create a grandchild which will watch
94 * and kill the grandparent. Other methods: 105 * and kill the grandparent. Other methods:
95 * making parent watch child disrupts parent<->child link 106 * making parent watch child disrupts parent<->child link
96 * (example: "tcpsvd 0.0.0.0 1234 timeout service_prog" - 107 * (example: "tcpsvd 0.0.0.0 1234 timeout service_prog" -
97 * it's better if service_prog is a child of tcpsvd!), 108 * it's better if service_prog is a child of tcpsvd!),
98 * making child watch parent results in programs having 109 * making child watch parent results in programs having
99 * unexpected children. */ 110 * unexpected children. */
100 111
101 if (parent) /* we were re-execed, already grandchild */ 112 if (parent) /* we were re-execed, already grandchild */
102 goto grandchild; 113 goto grandchild;
103 if (!argv[optind]) /* no PROG? */
104 bb_show_usage();
105 114
106#if !BB_MMU 115#if !BB_MMU
107 sv1 = argv[optind]; 116 sv1 = argv[optind];
@@ -146,13 +155,6 @@ int timeout_main(int argc UNUSED_PARAM, char **argv)
146#endif 155#endif
147 BB_EXECVP_or_die(argv); 156 BB_EXECVP_or_die(argv);
148#else /* ENABLE_PLATFORM_MINGW32 */ 157#else /* ENABLE_PLATFORM_MINGW32 */
149 if (signo != SIGTERM && signo != SIGKILL && signo != 0)
150 bb_error_msg_and_die("unknown signal '%s'", opt_s);
151
152 argv += optind;
153 if (argv[0] == NULL)
154 bb_show_usage();
155
156 if ((ret=mingw_spawn_proc((const char **)argv)) == -1) { 158 if ((ret=mingw_spawn_proc((const char **)argv)) == -1) {
157 xfunc_error_retval = errno == EACCES ? 126 : 127; 159 xfunc_error_retval = errno == EACCES ? 126 : 127;
158 bb_perror_msg_and_die("can't execute '%s'", argv[0]); 160 bb_perror_msg_and_die("can't execute '%s'", argv[0]);
diff --git a/examples/var_service/ntpd/ntp.script b/examples/var_service/ntpd/ntp.script
index 8542181eb..6cd81014d 100755
--- a/examples/var_service/ntpd/ntp.script
+++ b/examples/var_service/ntpd/ntp.script
@@ -1,5 +1,4 @@
1#!/bin/sh 1#!/bin/sh
2
3# Note that there is no provision to prevent several copies of the script 2# Note that there is no provision to prevent several copies of the script
4# to be run in quick succession. In fact, it happens rather often 3# to be run in quick succession. In fact, it happens rather often
5# if initial syncronization results in a step. 4# if initial syncronization results in a step.
@@ -8,6 +7,9 @@
8# 7#
9# Script should be ready to deal with this. 8# Script should be ready to deal with this.
10 9
10# For other parts of the system which prefer to run only on the stable clock
11echo "$1" >rundir/sync_status
12
11dt=`date '+%Y-%m-%d %H:%M:%S'` 13dt=`date '+%Y-%m-%d %H:%M:%S'`
12 14
13echo "`tail -n 199 -- "$0.log" 2>/dev/null`" >"$0.log.$$" 15echo "`tail -n 199 -- "$0.log" 2>/dev/null`" >"$0.log.$$"
diff --git a/include/libbb.h b/include/libbb.h
index 0264282dd..0d09fef66 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1062,6 +1062,14 @@ int xatoi_positive(const char *numstr) FAST_FUNC;
1062/* Useful for reading port numbers */ 1062/* Useful for reading port numbers */
1063uint16_t xatou16(const char *numstr) FAST_FUNC; 1063uint16_t xatou16(const char *numstr) FAST_FUNC;
1064 1064
1065#if ENABLE_FLOAT_DURATION
1066typedef double duration_t;
1067void sleep_for_duration(duration_t duration) FAST_FUNC;
1068#else
1069typedef unsigned duration_t;
1070#define sleep_for_duration(duration) sleep(duration)
1071#endif
1072duration_t parse_duration_str(char *str) FAST_FUNC;
1065 1073
1066/* These parse entries in /etc/passwd and /etc/group. This is desirable 1074/* These parse entries in /etc/passwd and /etc/group. This is desirable
1067 * for BusyBox since we want to avoid using the glibc NSS stuff, which 1075 * for BusyBox since we want to avoid using the glibc NSS stuff, which
diff --git a/libbb/copy_file.c b/libbb/copy_file.c
index 7cd9cd978..299cd7bea 100644
--- a/libbb/copy_file.c
+++ b/libbb/copy_file.c
@@ -391,14 +391,15 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
391 char *lpath = xmalloc_readlink_or_warn(source); 391 char *lpath = xmalloc_readlink_or_warn(source);
392 if (lpath) { 392 if (lpath) {
393 int r = symlink(lpath, dest); 393 int r = symlink(lpath, dest);
394 free(lpath);
395 if (r < 0) { 394 if (r < 0) {
396 /* shared message */ 395 /* shared message */
397 bb_perror_msg("can't create %slink '%s' to '%s'", 396 bb_perror_msg("can't create %slink '%s' to '%s'",
398 "sym", dest, lpath 397 "sym", dest, lpath
399 ); 398 );
399 free(lpath);
400 return -1; 400 return -1;
401 } 401 }
402 free(lpath);
402 if (flags & FILEUTILS_PRESERVE_STATUS) 403 if (flags & FILEUTILS_PRESERVE_STATUS)
403 if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0) 404 if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
404 bb_perror_msg("can't preserve %s of '%s'", "ownership", dest); 405 bb_perror_msg("can't preserve %s of '%s'", "ownership", dest);
diff --git a/libbb/duration.c b/libbb/duration.c
new file mode 100644
index 000000000..5acd0dba3
--- /dev/null
+++ b/libbb/duration.c
@@ -0,0 +1,78 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 2018 Denys Vlasenko
6 *
7 * Licensed under GPLv2, see file LICENSE in this source tree.
8 */
9//config:config FLOAT_DURATION
10//config: bool "Enable fractional duration arguments"
11//config: default y
12//config: help
13//config: Allow sleep N.NNN, top -d N.NNN etc.
14
15//kbuild:lib-$(CONFIG_SLEEP) += duration.o
16//kbuild:lib-$(CONFIG_TOP) += duration.o
17//kbuild:lib-$(CONFIG_TIMEOUT) += duration.o
18//kbuild:lib-$(CONFIG_PING) += duration.o
19//kbuild:lib-$(CONFIG_PING6) += duration.o
20
21#include "libbb.h"
22
23static const struct suffix_mult duration_suffixes[] = {
24 { "s", 1 },
25 { "m", 60 },
26 { "h", 60*60 },
27 { "d", 24*60*60 },
28 { "", 0 }
29};
30
31#if ENABLE_FLOAT_DURATION
32duration_t FAST_FUNC parse_duration_str(char *str)
33{
34 duration_t duration;
35
36 if (strchr(str, '.')) {
37 double d;
38 char *pp;
39 int len = strspn(str, "0123456789.");
40 char sv = str[len];
41 str[len] = '\0';
42 errno = 0;
43 d = strtod(str, &pp);
44 if (errno || *pp)
45 bb_show_usage();
46 str += len;
47 *str-- = sv;
48 sv = *str;
49 *str = '1';
50 duration = d * xatoul_sfx(str, duration_suffixes);
51 *str = sv;
52 } else {
53 duration = xatoul_sfx(str, duration_suffixes);
54 }
55
56 return duration;
57}
58void FAST_FUNC sleep_for_duration(duration_t duration)
59{
60 struct timespec ts;
61
62 ts.tv_sec = MAXINT(typeof(ts.tv_sec));
63 ts.tv_nsec = 0;
64 if (duration >= 0 && duration < ts.tv_sec) {
65 ts.tv_sec = duration;
66 ts.tv_nsec = (duration - ts.tv_sec) * 1000000000;
67 }
68 do {
69 errno = 0;
70 nanosleep(&ts, &ts);
71 } while (errno == EINTR);
72}
73#else
74duration_t FAST_FUNC parse_duration_str(char *str)
75{
76 return xatou_range_sfx(str, 0, UINT_MAX, duration_suffixes);
77}
78#endif
diff --git a/libbb/get_line_from_file.c b/libbb/get_line_from_file.c
index 09ccfba67..929bab78a 100644
--- a/libbb/get_line_from_file.c
+++ b/libbb/get_line_from_file.c
@@ -47,7 +47,9 @@ char* FAST_FUNC bb_get_chunk_from_file(FILE *file, size_t *end)
47/* Get line, including trailing \n if any */ 47/* Get line, including trailing \n if any */
48char* FAST_FUNC xmalloc_fgets(FILE *file) 48char* FAST_FUNC xmalloc_fgets(FILE *file)
49{ 49{
50 return bb_get_chunk_from_file(file, NULL); 50 size_t i;
51
52 return bb_get_chunk_from_file(file, &i);
51} 53}
52/* Get line. Remove trailing \n */ 54/* Get line. Remove trailing \n */
53char* FAST_FUNC xmalloc_fgetline(FILE *file) 55char* FAST_FUNC xmalloc_fgetline(FILE *file)
diff --git a/libbb/remove_file.c b/libbb/remove_file.c
index 86c9a5c56..cea5d47e6 100644
--- a/libbb/remove_file.c
+++ b/libbb/remove_file.c
@@ -73,7 +73,7 @@ int FAST_FUNC remove_file(const char *path, int flags)
73 return status; 73 return status;
74 } 74 }
75 75
76 if (rmdir(path) < 0) { 76 if (status == 0 && rmdir(path) < 0) {
77 bb_perror_msg("can't remove '%s'", path); 77 bb_perror_msg("can't remove '%s'", path);
78 return -1; 78 return -1;
79 } 79 }
diff --git a/libbb/unicode.c b/libbb/unicode.c
index 9c4da50d3..d378175a4 100644
--- a/libbb/unicode.c
+++ b/libbb/unicode.c
@@ -1121,6 +1121,8 @@ static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char
1121 dst[dst_len++] = ' '; 1121 dst[dst_len++] = ' ';
1122 } 1122 }
1123 } 1123 }
1124 if (!dst) /* for example, if input was "" */
1125 dst = xzalloc(1);
1124 dst[dst_len] = '\0'; 1126 dst[dst_len] = '\0';
1125 if (stats) { 1127 if (stats) {
1126 stats->byte_count = dst_len; 1128 stats->byte_count = dst_len;
diff --git a/loginutils/chpasswd.c b/loginutils/chpasswd.c
index 652e4f127..4b3602e7a 100644
--- a/loginutils/chpasswd.c
+++ b/loginutils/chpasswd.c
@@ -24,18 +24,20 @@
24//kbuild:lib-$(CONFIG_CHPASSWD) += chpasswd.o 24//kbuild:lib-$(CONFIG_CHPASSWD) += chpasswd.o
25 25
26//usage:#define chpasswd_trivial_usage 26//usage:#define chpasswd_trivial_usage
27//usage: IF_LONG_OPTS("[--md5|--encrypted|--crypt-method]") IF_NOT_LONG_OPTS("[-m|-e|-c]") 27//usage: IF_LONG_OPTS("[--md5|--encrypted|--crypt-method|--root]") IF_NOT_LONG_OPTS("[-m|-e|-c|-R]")
28//usage:#define chpasswd_full_usage "\n\n" 28//usage:#define chpasswd_full_usage "\n\n"
29//usage: "Read user:password from stdin and update /etc/passwd\n" 29//usage: "Read user:password from stdin and update /etc/passwd\n"
30//usage: IF_LONG_OPTS( 30//usage: IF_LONG_OPTS(
31//usage: "\n -e,--encrypted Supplied passwords are in encrypted form" 31//usage: "\n -e,--encrypted Supplied passwords are in encrypted form"
32//usage: "\n -m,--md5 Eencrypt using md5, not des" 32//usage: "\n -m,--md5 Encrypt using md5, not des"
33//usage: "\n -c,--crypt-method ALG "CRYPT_METHODS_HELP_STR 33//usage: "\n -c,--crypt-method ALG "CRYPT_METHODS_HELP_STR
34//usage: "\n -R,--root DIR Directory to chroot into"
34//usage: ) 35//usage: )
35//usage: IF_NOT_LONG_OPTS( 36//usage: IF_NOT_LONG_OPTS(
36//usage: "\n -e Supplied passwords are in encrypted form" 37//usage: "\n -e Supplied passwords are in encrypted form"
37//usage: "\n -m Eencrypt using md5, not des" 38//usage: "\n -m Encrypt using md5, not des"
38//usage: "\n -c ALG "CRYPT_METHODS_HELP_STR 39//usage: "\n -c ALG "CRYPT_METHODS_HELP_STR
40//usage: "\n -R DIR Directory to chroot into"
39//usage: ) 41//usage: )
40 42
41#include "libbb.h" 43#include "libbb.h"
@@ -45,6 +47,7 @@ static const char chpasswd_longopts[] ALIGN1 =
45 "encrypted\0" No_argument "e" 47 "encrypted\0" No_argument "e"
46 "md5\0" No_argument "m" 48 "md5\0" No_argument "m"
47 "crypt-method\0" Required_argument "c" 49 "crypt-method\0" Required_argument "c"
50 "root\0" Required_argument "R"
48 ; 51 ;
49#endif 52#endif
50 53
@@ -56,16 +59,21 @@ int chpasswd_main(int argc UNUSED_PARAM, char **argv)
56{ 59{
57 char *name; 60 char *name;
58 const char *algo = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO; 61 const char *algo = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO;
62 const char *root = NULL;
59 int opt; 63 int opt;
60 64
61 if (getuid() != 0) 65 if (getuid() != 0)
62 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); 66 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
63 67
64 opt = getopt32long(argv, "^" "emc:" "\0" "m--ec:e--mc:c--em", 68 opt = getopt32long(argv, "^" "emc:R:" "\0" "m--ec:e--mc:c--em",
65 chpasswd_longopts, 69 chpasswd_longopts,
66 &algo 70 &algo, &root
67 ); 71 );
68 72
73 if (root) {
74 xchroot(root);
75 }
76
69 while ((name = xmalloc_fgetline(stdin)) != NULL) { 77 while ((name = xmalloc_fgetline(stdin)) != NULL) {
70 char *free_me; 78 char *free_me;
71 char *pass; 79 char *pass;
diff --git a/mailutils/mail.c b/mailutils/mail.c
index 7af7edd6c..6726654f7 100644
--- a/mailutils/mail.c
+++ b/mailutils/mail.c
@@ -107,7 +107,7 @@ static char* FAST_FUNC parse_url(char *url, char **user, char **pass)
107} 107}
108*/ 108*/
109 109
110void FAST_FUNC encode_base64(char *fname, const char *text, const char *eol) 110static void encode_n_base64(const char *fname, const char *text, size_t len)
111{ 111{
112 enum { 112 enum {
113 SRC_BUF_SIZE = 57, /* This *MUST* be a multiple of 3 */ 113 SRC_BUF_SIZE = 57, /* This *MUST* be a multiple of 3 */
@@ -116,18 +116,12 @@ void FAST_FUNC encode_base64(char *fname, const char *text, const char *eol)
116#define src_buf text 116#define src_buf text
117 char src[SRC_BUF_SIZE]; 117 char src[SRC_BUF_SIZE];
118 FILE *fp = fp; 118 FILE *fp = fp;
119 ssize_t len = len;
120 char dst_buf[DST_BUF_SIZE + 1]; 119 char dst_buf[DST_BUF_SIZE + 1];
121 120
122 if (fname) { 121 if (fname) {
123 fp = (NOT_LONE_DASH(fname)) ? xfopen_for_read(fname) : (FILE *)text; 122 fp = (NOT_LONE_DASH(fname)) ? xfopen_for_read(fname) : stdin;
124 src_buf = src; 123 src_buf = src;
125 } else if (text) { 124 }
126 // though we do not call uuencode(NULL, NULL) explicitly
127 // still we do not want to break things suddenly
128 len = strlen(text);
129 } else
130 return;
131 125
132 while (1) { 126 while (1) {
133 size_t size; 127 size_t size;
@@ -145,7 +139,7 @@ void FAST_FUNC encode_base64(char *fname, const char *text, const char *eol)
145 // encode the buffer we just read in 139 // encode the buffer we just read in
146 bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64); 140 bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64);
147 if (fname) { 141 if (fname) {
148 puts(eol); 142 puts("");
149 } else { 143 } else {
150 src_buf += size; 144 src_buf += size;
151 len -= size; 145 len -= size;
@@ -157,6 +151,21 @@ void FAST_FUNC encode_base64(char *fname, const char *text, const char *eol)
157#undef src_buf 151#undef src_buf
158} 152}
159 153
154void FAST_FUNC printstr_base64(const char *text)
155{
156 encode_n_base64(NULL, text, strlen(text));
157}
158
159void FAST_FUNC printbuf_base64(const char *text, unsigned len)
160{
161 encode_n_base64(NULL, text, len);
162}
163
164void FAST_FUNC printfile_base64(const char *fname)
165{
166 encode_n_base64(fname, NULL, 0);
167}
168
160/* 169/*
161 * get username and password from a file descriptor 170 * get username and password from a file descriptor
162 */ 171 */
diff --git a/mailutils/mail.h b/mailutils/mail.h
index fa0c5b378..b14228a4a 100644
--- a/mailutils/mail.h
+++ b/mailutils/mail.h
@@ -34,4 +34,6 @@ void get_cred_or_die(int fd) FAST_FUNC;
34 34
35char *send_mail_command(const char *fmt, const char *param) FAST_FUNC; 35char *send_mail_command(const char *fmt, const char *param) FAST_FUNC;
36 36
37void encode_base64(char *fname, const char *text, const char *eol) FAST_FUNC; 37void printbuf_base64(const char *buf, unsigned len) FAST_FUNC;
38void printstr_base64(const char *buf) FAST_FUNC;
39void printfile_base64(const char *fname) FAST_FUNC;
diff --git a/mailutils/makemime.c b/mailutils/makemime.c
index 577bcde39..7539d5134 100644
--- a/mailutils/makemime.c
+++ b/mailutils/makemime.c
@@ -234,7 +234,7 @@ int makemime_main(int argc UNUSED_PARAM, char **argv)
234 , G.opt_charset 234 , G.opt_charset
235 , bb_get_last_path_component_strip(*argv) 235 , bb_get_last_path_component_strip(*argv)
236 ); 236 );
237 encode_base64(*argv++, (const char *)stdin, ""); 237 printfile_base64(*argv++);
238 } 238 }
239 239
240 // put multipart footer 240 // put multipart footer
diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c
index 0170f2870..32c50ba84 100644
--- a/mailutils/sendmail.c
+++ b/mailutils/sendmail.c
@@ -36,7 +36,9 @@
36//usage: "\n openssl s_client -quiet -tls1 -connect smtp.gmail.com:465" 36//usage: "\n openssl s_client -quiet -tls1 -connect smtp.gmail.com:465"
37//usage: "\n $SMTP_ANTISPAM_DELAY: seconds to wait after helper connect" 37//usage: "\n $SMTP_ANTISPAM_DELAY: seconds to wait after helper connect"
38//usage: "\n -S HOST[:PORT] Server (default $SMTPHOST or 127.0.0.1)" 38//usage: "\n -S HOST[:PORT] Server (default $SMTPHOST or 127.0.0.1)"
39//usage: "\n -amLOGIN Log in using AUTH LOGIN (-amCRAM-MD5 not supported)" 39//usage: "\n -amLOGIN Log in using AUTH LOGIN"
40//usage: "\n -amPLAIN or AUTH PLAIN"
41//usage: "\n (-amCRAM-MD5 not supported)"
40//usage: "\n -auUSER Username for AUTH" 42//usage: "\n -auUSER Username for AUTH"
41//usage: "\n -apPASS Password for AUTH" 43//usage: "\n -apPASS Password for AUTH"
42//usage: "\n" 44//usage: "\n"
@@ -248,6 +250,8 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
248 OPT_S = 1 << 6, // specify connection string 250 OPT_S = 1 << 6, // specify connection string
249 OPT_a = 1 << 7, // authentication tokens 251 OPT_a = 1 << 7, // authentication tokens
250 OPT_v = 1 << 8, // verbosity 252 OPT_v = 1 << 8, // verbosity
253 //--- for -amMETHOD
254 OPT_am_plain = 1 << 9, // AUTH PLAIN
251 }; 255 };
252 256
253 // init global variables 257 // init global variables
@@ -286,9 +290,14 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
286 G.user = xstrdup(a+1); 290 G.user = xstrdup(a+1);
287 if ('p' == a[0]) 291 if ('p' == a[0])
288 G.pass = xstrdup(a+1); 292 G.pass = xstrdup(a+1);
289 // N.B. we support only AUTH LOGIN so far 293 if ('m' == a[0]) {
290 //if ('m' == a[0]) 294 if ((a[1] | 0x20) == 'p') // PLAIN
291 // G.method = xstrdup(a+1); 295 opts |= OPT_am_plain;
296 else if ((a[1] | 0x20) == 'l') // LOGIN
297 ; /* do nothing (this is the default) */
298 else
299 bb_error_msg_and_die("unsupported AUTH method %s", a+1);
300 }
292 } 301 }
293 // N.B. list == NULL here 302 // N.B. list == NULL here
294 //bb_error_msg("OPT[%x] AU[%s], AP[%s], AM[%s], ARGV[%s]", opts, au, ap, am, *argv); 303 //bb_error_msg("OPT[%x] AU[%s], AP[%s], AM[%s], ARGV[%s]", opts, au, ap, am, *argv);
@@ -348,13 +357,44 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
348 357
349 // perform authentication 358 // perform authentication
350 if (opts & OPT_a) { 359 if (opts & OPT_a) {
351 smtp_check("AUTH LOGIN", 334); 360 // read credentials unless they are given via -a[up] options
352 // we must read credentials unless they are given via -a[up] options
353 if (!G.user || !G.pass) 361 if (!G.user || !G.pass)
354 get_cred_or_die(4); 362 get_cred_or_die(4);
355 encode_base64(NULL, G.user, NULL); 363 if (opts & OPT_am_plain) {
356 smtp_check("", 334); 364 // C: AUTH PLAIN
357 encode_base64(NULL, G.pass, NULL); 365 // S: 334
366 // C: base64encoded(auth<NUL>user<NUL>pass)
367 // S: 235 2.7.0 Authentication successful
368//Note: a shorter format is allowed:
369// C: AUTH PLAIN base64encoded(auth<NUL>user<NUL>pass)
370// S: 235 2.7.0 Authentication successful
371 smtp_check("AUTH PLAIN", 334);
372 {
373 unsigned user_len = strlen(G.user);
374 unsigned pass_len = strlen(G.pass);
375 unsigned sz = 1 + user_len + 1 + pass_len;
376 char plain_auth[sz + 1];
377 // the format is:
378 // "authorization identity<NUL>username<NUL>password"
379 // authorization identity is empty.
380 plain_auth[0] = '\0';
381 strcpy(stpcpy(plain_auth + 1, G.user) + 1, G.pass);
382 printbuf_base64(plain_auth, sz);
383 }
384 } else {
385 // C: AUTH LOGIN
386 // S: 334 VXNlcm5hbWU6
387 // ^^^^^^^^^^^^ server says "Username:"
388 // C: base64encoded(user)
389 // S: 334 UGFzc3dvcmQ6
390 // ^^^^^^^^^^^^ server says "Password:"
391 // C: base64encoded(pass)
392 // S: 235 2.7.0 Authentication successful
393 smtp_check("AUTH LOGIN", 334);
394 printstr_base64(G.user);
395 smtp_check("", 334);
396 printstr_base64(G.pass);
397 }
358 smtp_check("", 235); 398 smtp_check("", 235);
359 } 399 }
360 400
diff --git a/networking/ip.c b/networking/ip.c
index 9ecb99abb..97d618cd9 100644
--- a/networking/ip.c
+++ b/networking/ip.c
@@ -267,8 +267,7 @@
267//--------------123456789.123456789.123456789.123456789.123456789.123456789.123456789.123....79 267//--------------123456789.123456789.123456789.123456789.123456789.123456789.123456789.123....79
268//usage:#define iptunnel_trivial_usage 268//usage:#define iptunnel_trivial_usage
269//usage: "add|change|del|show [NAME]\n" 269//usage: "add|change|del|show [NAME]\n"
270//usage: " [mode ipip|gre|sit]\n" 270//usage: " [mode ipip|gre|sit] [remote ADDR] [local ADDR] [ttl TTL]"
271//usage: " [remote ADDR] [local ADDR] [ttl TTL]"
272//usage:#define iptunnel_full_usage "\n\n" 271//usage:#define iptunnel_full_usage "\n\n"
273//usage: "iptunnel add|change|del|show [NAME]\n" 272//usage: "iptunnel add|change|del|show [NAME]\n"
274//usage: " [mode ipip|gre|sit] [remote ADDR] [local ADDR]\n" 273//usage: " [mode ipip|gre|sit] [remote ADDR] [local ADDR]\n"
@@ -308,10 +307,9 @@
308//usage: IF_FEATURE_IP_TUNNEL( IP_BAR_TUNNEL"tunnel") 307//usage: IF_FEATURE_IP_TUNNEL( IP_BAR_TUNNEL"tunnel")
309//usage: IF_FEATURE_IP_NEIGH( IP_BAR_NEIGH "neigh") 308//usage: IF_FEATURE_IP_NEIGH( IP_BAR_NEIGH "neigh")
310//usage: IF_FEATURE_IP_RULE( IP_BAR_RULE "rule") 309//usage: IF_FEATURE_IP_RULE( IP_BAR_RULE "rule")
311//usage: " [COMMAND]" 310//usage: " [ARGS]"
312//usage:#define ip_full_usage "\n\n" 311//usage:#define ip_full_usage "\n\n"
313//usage: "OPTIONS := -f[amily] inet|inet6|link | -o[neline]\n" 312//usage: "OPTIONS := -f[amily] inet|inet6|link | -o[neline]\n"
314//usage: "COMMAND :="
315//usage: IF_FEATURE_IP_ADDRESS("\n" 313//usage: IF_FEATURE_IP_ADDRESS("\n"
316//usage: "ip addr "ipaddr_trivial_usage) 314//usage: "ip addr "ipaddr_trivial_usage)
317//usage: IF_FEATURE_IP_ROUTE("\n" 315//usage: IF_FEATURE_IP_ROUTE("\n"
diff --git a/networking/nslookup.c b/networking/nslookup.c
index 3a614b0c6..e153eb585 100644
--- a/networking/nslookup.c
+++ b/networking/nslookup.c
@@ -318,6 +318,8 @@ struct globals {
318 unsigned serv_count; 318 unsigned serv_count;
319 struct ns *server; 319 struct ns *server;
320 struct query *query; 320 struct query *query;
321 char *search;
322 smalluint have_search_directive;
321} FIX_ALIASING; 323} FIX_ALIASING;
322#define G (*(struct globals*)bb_common_bufsiz1) 324#define G (*(struct globals*)bb_common_bufsiz1)
323#define INIT_G() do { \ 325#define INIT_G() do { \
@@ -667,24 +669,60 @@ static void parse_resolvconf(void)
667 669
668 resolv = fopen("/etc/resolv.conf", "r"); 670 resolv = fopen("/etc/resolv.conf", "r");
669 if (resolv) { 671 if (resolv) {
670 char line[128], *p; 672 char line[512]; /* "search" is defined to be up to 256 chars */
671 673
672 while (fgets(line, sizeof(line), resolv)) { 674 while (fgets(line, sizeof(line), resolv)) {
673 p = strtok(line, " \t\n"); 675 char *p, *arg;
674 676
675 if (!p || strcmp(p, "nameserver") != 0) 677 p = strtok(line, " \t\n");
678 if (!p)
679 continue;
680 dbg("resolv_key:'%s'\n", p);
681 arg = strtok(NULL, "\n");
682 dbg("resolv_arg:'%s'\n", arg);
683 if (!arg)
676 continue; 684 continue;
677 685
678 p = strtok(NULL, " \t\n"); 686 if (strcmp(p, "domain") == 0) {
687 /* domain DOM */
688 if (!G.have_search_directive)
689 goto set_search;
690 continue;
691 }
692 if (strcmp(p, "search") == 0) {
693 /* search DOM1 DOM2... */
694 G.have_search_directive = 1;
695 set_search:
696 free(G.search);
697 G.search = xstrdup(arg);
698 dbg("search='%s'\n", G.search);
699 continue;
700 }
679 701
680 if (!p) 702 if (strcmp(p, "nameserver") != 0)
681 continue; 703 continue;
682 704
683 add_ns(xstrdup(p)); 705 /* nameserver DNS */
706 add_ns(xstrdup(arg));
684 } 707 }
685 708
686 fclose(resolv); 709 fclose(resolv);
687 } 710 }
711
712 if (!G.search) {
713 /* default search domain is domain part of hostname */
714 char *h = safe_gethostname();
715 char *d = strchr(h, '.');
716 if (d) {
717 G.search = d + 1;
718 dbg("search='%s' (from hostname)\n", G.search);
719 }
720 /* else free(h); */
721 }
722
723 /* Cater for case of "domain ." in resolv.conf */
724 if (G.search && LONE_CHAR(G.search, '.'))
725 G.search = NULL;
688} 726}
689 727
690static void add_query(int type, const char *dname) 728static void add_query(int type, const char *dname)
@@ -695,7 +733,7 @@ static void add_query(int type, const char *dname)
695 733
696 count = G.query_count++; 734 count = G.query_count++;
697 735
698 G.query = xrealloc_vector(G.query, /*2=2^1:*/ 1, count); 736 G.query = xrealloc_vector(G.query, /*4=2^2:*/ 2, count);
699 new_q = &G.query[count]; 737 new_q = &G.query[count];
700 738
701 dbg("new query#%u type %u for '%s'\n", count, type, dname); 739 dbg("new query#%u type %u for '%s'\n", count, type, dname);
@@ -709,6 +747,28 @@ static void add_query(int type, const char *dname)
709 new_q->qlen = qlen; 747 new_q->qlen = qlen;
710} 748}
711 749
750static void add_query_with_search(int type, const char *dname)
751{
752 char *s;
753
754 if (type == T_PTR || !G.search || strchr(dname, '.')) {
755 add_query(type, dname);
756 return;
757 }
758
759 s = G.search;
760 for (;;) {
761 char *fullname, *e;
762
763 e = skip_non_whitespace(s);
764 fullname = xasprintf("%s.%.*s", dname, (int)(e - s), s);
765 add_query(type, fullname);
766 s = skip_whitespace(e);
767 if (!*s)
768 break;
769 }
770}
771
712static char *make_ptr(const char *addrstr) 772static char *make_ptr(const char *addrstr)
713{ 773{
714 unsigned char addr[16]; 774 unsigned char addr[16];
@@ -833,6 +893,18 @@ int nslookup_main(int argc UNUSED_PARAM, char **argv)
833 } 893 }
834 } 894 }
835 895
896 /* Use given DNS server if present */
897 if (argv[1]) {
898 if (argv[2])
899 bb_show_usage();
900 add_ns(argv[1]);
901 } else {
902 parse_resolvconf();
903 /* Fall back to localhost if we could not find NS in resolv.conf */
904 if (G.serv_count == 0)
905 add_ns("127.0.0.1");
906 }
907
836 if (types == 0) { 908 if (types == 0) {
837 /* No explicit type given, guess query type. 909 /* No explicit type given, guess query type.
838 * If we can convert the domain argument into a ptr (means that 910 * If we can convert the domain argument into a ptr (means that
@@ -846,31 +918,19 @@ int nslookup_main(int argc UNUSED_PARAM, char **argv)
846 if (ptr) { 918 if (ptr) {
847 add_query(T_PTR, ptr); 919 add_query(T_PTR, ptr);
848 } else { 920 } else {
849 add_query(T_A, argv[0]); 921 add_query_with_search(T_A, argv[0]);
850#if ENABLE_FEATURE_IPV6 922#if ENABLE_FEATURE_IPV6
851 add_query(T_AAAA, argv[0]); 923 add_query_with_search(T_AAAA, argv[0]);
852#endif 924#endif
853 } 925 }
854 } else { 926 } else {
855 int c; 927 int c;
856 for (c = 0; c < ARRAY_SIZE(qtypes); c++) { 928 for (c = 0; c < ARRAY_SIZE(qtypes); c++) {
857 if (types & (1 << c)) 929 if (types & (1 << c))
858 add_query(qtypes[c].type, argv[0]); 930 add_query_with_search(qtypes[c].type, argv[0]);
859 } 931 }
860 } 932 }
861 933
862 /* Use given DNS server if present */
863 if (argv[1]) {
864 if (argv[2])
865 bb_show_usage();
866 add_ns(argv[1]);
867 } else {
868 parse_resolvconf();
869 /* Fall back to localhost if we could not find NS in resolv.conf */
870 if (G.serv_count == 0)
871 add_ns("127.0.0.1");
872 }
873
874 for (rc = 0; rc < G.serv_count;) { 934 for (rc = 0; rc < G.serv_count;) {
875 int c; 935 int c;
876 936
diff --git a/networking/ntpd.c b/networking/ntpd.c
index 7b800369e..991c518f6 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -149,8 +149,8 @@
149 */ 149 */
150 150
151#define INITIAL_SAMPLES 4 /* how many samples do we want for init */ 151#define INITIAL_SAMPLES 4 /* how many samples do we want for init */
152#define MIN_FREQHOLD 10 /* adjust offset, but not freq in this many first adjustments */ 152#define MIN_FREQHOLD 12 /* adjust offset, but not freq in this many first adjustments */
153#define BAD_DELAY_GROWTH 4 /* drop packet if its delay grew by more than this */ 153#define BAD_DELAY_GROWTH 4 /* drop packet if its delay grew by more than this factor */
154 154
155#define RETRY_INTERVAL 32 /* on send/recv error, retry in N secs (need to be power of 2) */ 155#define RETRY_INTERVAL 32 /* on send/recv error, retry in N secs (need to be power of 2) */
156#define NOREPLY_INTERVAL 512 /* sent, but got no reply: cap next query by this many seconds */ 156#define NOREPLY_INTERVAL 512 /* sent, but got no reply: cap next query by this many seconds */
@@ -1777,9 +1777,9 @@ update_local_clock(peer_t *p)
1777//15:31:53.473 update from:<IP> offset:+0.000007 delay:0.158142 jitter:0.010922 clock drift:+9.343ppm tc:6 1777//15:31:53.473 update from:<IP> offset:+0.000007 delay:0.158142 jitter:0.010922 clock drift:+9.343ppm tc:6
1778//15:32:58.902 update from:<IP> offset:-0.000728 delay:0.158222 jitter:0.009454 clock drift:+9.298ppm tc:6 1778//15:32:58.902 update from:<IP> offset:-0.000728 delay:0.158222 jitter:0.009454 clock drift:+9.298ppm tc:6
1779 /* 1779 /*
1780 * This expression would choose MIN_FREQHOLD + 7 in the above example. 1780 * This expression would choose MIN_FREQHOLD + 8 in the above example.
1781 */ 1781 */
1782 G.FREQHOLD_cnt = MIN_FREQHOLD + ((unsigned)(abs(tmx.offset)) >> 16); 1782 G.FREQHOLD_cnt = 1 + MIN_FREQHOLD + ((unsigned)(abs(tmx.offset)) >> 16);
1783 } 1783 }
1784 G.FREQHOLD_cnt--; 1784 G.FREQHOLD_cnt--;
1785 tmx.status |= STA_FREQHOLD; 1785 tmx.status |= STA_FREQHOLD;
@@ -1819,7 +1819,7 @@ update_local_clock(peer_t *p)
1819 VERB2 bb_error_msg("update from:%s offset:%+f delay:%f jitter:%f clock drift:%+.3fppm tc:%d", 1819 VERB2 bb_error_msg("update from:%s offset:%+f delay:%f jitter:%f clock drift:%+.3fppm tc:%d",
1820 p->p_dotted, 1820 p->p_dotted,
1821 offset, 1821 offset,
1822 p->lastpkt_delay, 1822 p->p_raw_delay,
1823 G.discipline_jitter, 1823 G.discipline_jitter,
1824 (double)tmx.freq / 65536, 1824 (double)tmx.freq / 65536,
1825 (int)tmx.constant 1825 (int)tmx.constant
@@ -1976,27 +1976,30 @@ recv_and_process_peer_pkt(peer_t *p)
1976 T2 = lfp_to_d(msg.m_rectime); 1976 T2 = lfp_to_d(msg.m_rectime);
1977 T3 = lfp_to_d(msg.m_xmttime); 1977 T3 = lfp_to_d(msg.m_xmttime);
1978 T4 = G.cur_time; 1978 T4 = G.cur_time;
1979
1980 /* The delay calculation is a special case. In cases where the
1981 * server and client clocks are running at different rates and
1982 * with very fast networks, the delay can appear negative. In
1983 * order to avoid violating the Principle of Least Astonishment,
1984 * the delay is clamped not less than the system precision.
1985 */
1986 delay = (T4 - T1) - (T3 - T2); 1979 delay = (T4 - T1) - (T3 - T2);
1987 if (delay < G_precision_sec) 1980
1988 delay = G_precision_sec;
1989 /* 1981 /*
1990 * If this packet's delay is much bigger than the last one, 1982 * If this packet's delay is much bigger than the last one,
1991 * it's better to just ignore it than use its much less precise value. 1983 * it's better to just ignore it than use its much less precise value.
1992 */ 1984 */
1993 prev_delay = p->p_raw_delay; 1985 prev_delay = p->p_raw_delay;
1994 p->p_raw_delay = delay; 1986 p->p_raw_delay = (delay < 0 ? 0.0 : delay);
1995 if (p->reachable_bits && delay > prev_delay * BAD_DELAY_GROWTH) { 1987 if (p->reachable_bits
1988 && delay > prev_delay * BAD_DELAY_GROWTH
1989 && delay > 1.0 / (8 * 1024) /* larger than ~0.000122 */
1990 ) {
1996 bb_error_msg("reply from %s: delay %f is too high, ignoring", p->p_dotted, delay); 1991 bb_error_msg("reply from %s: delay %f is too high, ignoring", p->p_dotted, delay);
1997 goto pick_normal_interval; 1992 goto pick_normal_interval;
1998 } 1993 }
1999 1994
1995 /* The delay calculation is a special case. In cases where the
1996 * server and client clocks are running at different rates and
1997 * with very fast networks, the delay can appear negative. In
1998 * order to avoid violating the Principle of Least Astonishment,
1999 * the delay is clamped not less than the system precision.
2000 */
2001 if (delay < G_precision_sec)
2002 delay = G_precision_sec;
2000 p->lastpkt_delay = delay; 2003 p->lastpkt_delay = delay;
2001 p->lastpkt_recv_time = T4; 2004 p->lastpkt_recv_time = T4;
2002 VERB6 bb_error_msg("%s->lastpkt_recv_time=%f", p->p_dotted, p->lastpkt_recv_time); 2005 VERB6 bb_error_msg("%s->lastpkt_recv_time=%f", p->p_dotted, p->lastpkt_recv_time);
@@ -2024,7 +2027,7 @@ recv_and_process_peer_pkt(peer_t *p)
2024 bb_error_msg("reply from %s: offset:%+f delay:%f status:0x%02x strat:%d refid:0x%08x rootdelay:%f reach:0x%02x", 2027 bb_error_msg("reply from %s: offset:%+f delay:%f status:0x%02x strat:%d refid:0x%08x rootdelay:%f reach:0x%02x",
2025 p->p_dotted, 2028 p->p_dotted,
2026 offset, 2029 offset,
2027 p->lastpkt_delay, 2030 p->p_raw_delay,
2028 p->lastpkt_status, 2031 p->lastpkt_status,
2029 p->lastpkt_stratum, 2032 p->lastpkt_stratum,
2030 p->lastpkt_refid, 2033 p->lastpkt_refid,
diff --git a/networking/ping.c b/networking/ping.c
index 8f85d3ec2..570184fee 100644
--- a/networking/ping.c
+++ b/networking/ping.c
@@ -74,6 +74,7 @@
74//usage: ) 74//usage: )
75//usage: "\n -c CNT Send only CNT pings" 75//usage: "\n -c CNT Send only CNT pings"
76//usage: "\n -s SIZE Send SIZE data bytes in packets (default 56)" 76//usage: "\n -s SIZE Send SIZE data bytes in packets (default 56)"
77//usage: "\n -i SECS Interval"
77//usage: "\n -A Ping as soon as reply is recevied" 78//usage: "\n -A Ping as soon as reply is recevied"
78//usage: "\n -t TTL Set TTL" 79//usage: "\n -t TTL Set TTL"
79//usage: "\n -I IFACE/IP Source interface or IP address" 80//usage: "\n -I IFACE/IP Source interface or IP address"
@@ -91,6 +92,7 @@
91//usage: "Send ICMP ECHO_REQUEST packets to network hosts\n" 92//usage: "Send ICMP ECHO_REQUEST packets to network hosts\n"
92//usage: "\n -c CNT Send only CNT pings" 93//usage: "\n -c CNT Send only CNT pings"
93//usage: "\n -s SIZE Send SIZE data bytes in packets (default 56)" 94//usage: "\n -s SIZE Send SIZE data bytes in packets (default 56)"
95//usage: "\n -i SECS Interval"
94//usage: "\n -A Ping as soon as reply is recevied" 96//usage: "\n -A Ping as soon as reply is recevied"
95//usage: "\n -I IFACE/IP Source interface or IP address" 97//usage: "\n -I IFACE/IP Source interface or IP address"
96//usage: "\n -q Quiet, only display output at start" 98//usage: "\n -q Quiet, only display output at start"
@@ -350,7 +352,7 @@ static int common_ping_main(sa_family_t af, char **argv)
350/* Full(er) version */ 352/* Full(er) version */
351 353
352/* -c NUM, -t NUM, -w NUM, -W NUM */ 354/* -c NUM, -t NUM, -w NUM, -W NUM */
353#define OPT_STRING "qvAc:+s:t:+w:+W:+I:np:4"IF_PING6("6") 355#define OPT_STRING "qvAc:+s:t:+w:+W:+I:np:i:4"IF_PING6("6")
354enum { 356enum {
355 OPT_QUIET = 1 << 0, 357 OPT_QUIET = 1 << 0,
356 OPT_VERBOSE = 1 << 1, 358 OPT_VERBOSE = 1 << 1,
@@ -363,8 +365,9 @@ enum {
363 OPT_I = 1 << 8, 365 OPT_I = 1 << 8,
364 /*OPT_n = 1 << 9, - ignored */ 366 /*OPT_n = 1 << 9, - ignored */
365 OPT_p = 1 << 10, 367 OPT_p = 1 << 10,
366 OPT_IPV4 = 1 << 11, 368 OPT_i = 1 << 11,
367 OPT_IPV6 = (1 << 12) * ENABLE_PING6, 369 OPT_IPV4 = 1 << 12,
370 OPT_IPV6 = (1 << 13) * ENABLE_PING6,
368}; 371};
369 372
370 373
@@ -382,6 +385,7 @@ struct globals {
382 unsigned long long tsum; /* in us, sum of all times */ 385 unsigned long long tsum; /* in us, sum of all times */
383 unsigned cur_us; /* low word only, we don't need more */ 386 unsigned cur_us; /* low word only, we don't need more */
384 unsigned deadline_us; 387 unsigned deadline_us;
388 unsigned interval_us;
385 unsigned timeout; 389 unsigned timeout;
386 unsigned sizeof_rcv_packet; 390 unsigned sizeof_rcv_packet;
387 char *rcv_packet; /* [datalen + MAXIPLEN + MAXICMPLEN] */ 391 char *rcv_packet; /* [datalen + MAXIPLEN + MAXICMPLEN] */
@@ -481,9 +485,15 @@ static void sendping_tail(void (*sp)(int), int size_pkt)
481 bb_error_msg_and_die(bb_msg_write_error); 485 bb_error_msg_and_die(bb_msg_write_error);
482 486
483 if (pingcount == 0 || G.ntransmitted < pingcount) { 487 if (pingcount == 0 || G.ntransmitted < pingcount) {
484 /* Didn't send all pings yet - schedule next in 1s */ 488 /* Didn't send all pings yet - schedule next in -i SEC interval */
489 struct itimerval i;
485 signal(SIGALRM, sp); 490 signal(SIGALRM, sp);
486 alarm(PINGINTERVAL); 491 /*ualarm(G.interval_us, 0); - does not work for >=1sec on some libc */
492 i.it_interval.tv_sec = 0;
493 i.it_interval.tv_usec = 0;
494 i.it_value.tv_sec = G.interval_us / 1000000;
495 i.it_value.tv_usec = G.interval_us % 1000000;
496 setitimer(ITIMER_REAL, &i, NULL);
487 } else { /* -c NN, and all NN are sent */ 497 } else { /* -c NN, and all NN are sent */
488 /* Wait for the last ping to come back. 498 /* Wait for the last ping to come back.
489 * -W timeout: wait for a response in seconds. 499 * -W timeout: wait for a response in seconds.
@@ -885,6 +895,8 @@ static int common_ping_main(int opt, char **argv)
885{ 895{
886 len_and_sockaddr *lsa; 896 len_and_sockaddr *lsa;
887 char *str_s, *str_p; 897 char *str_s, *str_p;
898 char *str_i = (char*)"1";
899 duration_t interval;
888 900
889 INIT_G(); 901 INIT_G();
890 902
@@ -892,7 +904,7 @@ static int common_ping_main(int opt, char **argv)
892 OPT_STRING 904 OPT_STRING
893 /* exactly one arg; -v and -q don't mix */ 905 /* exactly one arg; -v and -q don't mix */
894 "\0" "=1:q--v:v--q", 906 "\0" "=1:q--v:v--q",
895 &pingcount, &str_s, &opt_ttl, &G.deadline_us, &timeout, &str_I, &str_p 907 &pingcount, &str_s, &opt_ttl, &G.deadline_us, &timeout, &str_I, &str_p, &str_i
896 ); 908 );
897 if (opt & OPT_s) 909 if (opt & OPT_s)
898 datalen = xatou16(str_s); // -s 910 datalen = xatou16(str_s); // -s
@@ -910,6 +922,10 @@ static int common_ping_main(int opt, char **argv)
910 unsigned d = G.deadline_us < INT_MAX/1000000 ? G.deadline_us : INT_MAX/1000000; 922 unsigned d = G.deadline_us < INT_MAX/1000000 ? G.deadline_us : INT_MAX/1000000;
911 G.deadline_us = 1 | ((d * 1000000) + monotonic_us()); 923 G.deadline_us = 1 | ((d * 1000000) + monotonic_us());
912 } 924 }
925 interval = parse_duration_str(str_i);
926 if (interval > INT_MAX/1000000)
927 interval = INT_MAX/1000000;
928 G.interval_us = interval * 1000000;
913 929
914 myid = (uint16_t) getpid(); 930 myid = (uint16_t) getpid();
915 hostname = argv[optind]; 931 hostname = argv[optind];
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index c2805a009..0310663e0 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -160,8 +160,8 @@ static int mton(uint32_t mask)
160 160
161#if ENABLE_FEATURE_UDHCPC_SANITIZEOPT 161#if ENABLE_FEATURE_UDHCPC_SANITIZEOPT
162/* Check if a given label represents a valid DNS label 162/* Check if a given label represents a valid DNS label
163 * Return pointer to the first character after the label upon success, 163 * Return pointer to the first character after the label
164 * NULL otherwise. 164 * (NUL or dot) upon success, NULL otherwise.
165 * See RFC1035, 2.3.1 165 * See RFC1035, 2.3.1
166 */ 166 */
167/* We don't need to be particularly anal. For example, allowing _, hyphen 167/* We don't need to be particularly anal. For example, allowing _, hyphen
@@ -173,8 +173,10 @@ static int mton(uint32_t mask)
173static const char *valid_domain_label(const char *label) 173static const char *valid_domain_label(const char *label)
174{ 174{
175 unsigned char ch; 175 unsigned char ch;
176 unsigned pos = 0; 176 //unsigned pos = 0;
177 177
178 if (label[0] == '-')
179 return NULL;
178 for (;;) { 180 for (;;) {
179 ch = *label; 181 ch = *label;
180 if ((ch|0x20) < 'a' || (ch|0x20) > 'z') { 182 if ((ch|0x20) < 'a' || (ch|0x20) > 'z') {
@@ -187,7 +189,7 @@ static const char *valid_domain_label(const char *label)
187 } 189 }
188 } 190 }
189 label++; 191 label++;
190 pos++; 192 //pos++;
191 //Do we want this? 193 //Do we want this?
192 //if (pos > 63) /* NS_MAXLABEL; labels must be 63 chars or less */ 194 //if (pos > 63) /* NS_MAXLABEL; labels must be 63 chars or less */
193 // return NULL; 195 // return NULL;
@@ -272,6 +274,12 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_
272 case OPTION_STRING_HOST: 274 case OPTION_STRING_HOST:
273 memcpy(dest, option, len); 275 memcpy(dest, option, len);
274 dest[len] = '\0'; 276 dest[len] = '\0';
277//TODO: it appears option 15 DHCP_DOMAIN_NAME is often abused
278//by DHCP admins to contain a space-separated list of domains,
279//not one domain name (presumably, to work as list of search domains,
280//instead of using proper option 119 DHCP_DOMAIN_SEARCH).
281//Currently, good_hostname() balks on strings containing spaces.
282//Do we need to allow it? Only for DHCP_DOMAIN_NAME option?
275 if (type == OPTION_STRING_HOST && !good_hostname(dest)) 283 if (type == OPTION_STRING_HOST && !good_hostname(dest))
276 safe_strncpy(dest, "bad", len); 284 safe_strncpy(dest, "bad", len);
277 return ret; 285 return ret;
diff --git a/networking/whois.c b/networking/whois.c
index f0ec86301..f3da32b4e 100644
--- a/networking/whois.c
+++ b/networking/whois.c
@@ -39,20 +39,26 @@ static char *query(const char *host, int port, const char *domain)
39 bool success; 39 bool success;
40 char *redir = NULL; 40 char *redir = NULL;
41 const char *pfx = ""; 41 const char *pfx = "";
42 char linebuf[1024]; 42 /* some .io domains reported to have very long strings in whois
43 * responses, 1k was not enough:
44 */
45 char linebuf[2 * 1024];
43 char *buf = NULL; 46 char *buf = NULL;
44 unsigned bufpos = 0; 47 unsigned bufpos = 0;
45 48
46 again: 49 again:
47 printf("[Querying %s:%d '%s%s']\n", host, port, pfx, domain); 50 printf("[Querying %s:%d '%s%s']\n", host, port, pfx, domain);
48 fd = create_and_connect_stream_or_die(host, port); 51 fd = create_and_connect_stream_or_die(host, port);
49 success = 0;
50 fdprintf(fd, "%s%s\r\n", pfx, domain); 52 fdprintf(fd, "%s%s\r\n", pfx, domain);
51 fp = xfdopen_for_read(fd); 53 fp = xfdopen_for_read(fd);
52 54
53 while (fgets(linebuf, sizeof(linebuf), fp)) { 55 success = 0;
54 unsigned len = strcspn(linebuf, "\r\n"); 56 while (fgets(linebuf, sizeof(linebuf)-1, fp)) {
57 unsigned len;
58
59 len = strcspn(linebuf, "\r\n");
55 linebuf[len++] = '\n'; 60 linebuf[len++] = '\n';
61 linebuf[len] = '\0';
56 62
57 buf = xrealloc(buf, bufpos + len + 1); 63 buf = xrealloc(buf, bufpos + len + 1);
58 memcpy(buf + bufpos, linebuf, len); 64 memcpy(buf + bufpos, linebuf, len);
diff --git a/procps/smemcap.c b/procps/smemcap.c
index f2e2de29d..01acbf74e 100644
--- a/procps/smemcap.c
+++ b/procps/smemcap.c
@@ -66,7 +66,11 @@ static void archivefile(const char *path)
66 struct stat s; 66 struct stat s;
67 67
68 /* buffer the file */ 68 /* buffer the file */
69 fd = xopen(path, O_RDONLY); 69 fd = open(path, O_RDONLY);
70 if (fd == -1) {
71 /* skip vanished processes between dir listing and traversal */
72 return;
73 }
70 do { 74 do {
71 cur = xzalloc(sizeof(*cur)); 75 cur = xzalloc(sizeof(*cur));
72 *prev = cur; 76 *prev = cur;
diff --git a/procps/top.c b/procps/top.c
index 1b49a5e6b..f016f5501 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -901,11 +901,11 @@ enum {
901}; 901};
902 902
903#if ENABLE_FEATURE_TOP_INTERACTIVE 903#if ENABLE_FEATURE_TOP_INTERACTIVE
904static unsigned handle_input(unsigned scan_mask, unsigned interval) 904static unsigned handle_input(unsigned scan_mask, duration_t interval)
905{ 905{
906 if (option_mask32 & OPT_EOF) { 906 if (option_mask32 & OPT_EOF) {
907 /* EOF on stdin ("top </dev/null") */ 907 /* EOF on stdin ("top </dev/null") */
908 sleep(interval); 908 sleep_for_duration(interval);
909 return scan_mask; 909 return scan_mask;
910 } 910 }
911 911
@@ -1092,9 +1092,9 @@ static unsigned handle_input(unsigned scan_mask, unsigned interval)
1092int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1092int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1093int top_main(int argc UNUSED_PARAM, char **argv) 1093int top_main(int argc UNUSED_PARAM, char **argv)
1094{ 1094{
1095 duration_t interval;
1095 int iterations; 1096 int iterations;
1096 unsigned col; 1097 unsigned col;
1097 unsigned interval;
1098 char *str_interval, *str_iterations; 1098 char *str_interval, *str_iterations;
1099 unsigned scan_mask = TOP_MASK; 1099 unsigned scan_mask = TOP_MASK;
1100 1100
@@ -1120,8 +1120,10 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1120 /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */ 1120 /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */
1121 if (str_interval[0] == '-') 1121 if (str_interval[0] == '-')
1122 str_interval++; 1122 str_interval++;
1123 interval = parse_duration_str(str_interval);
1123 /* Need to limit it to not overflow poll timeout */ 1124 /* Need to limit it to not overflow poll timeout */
1124 interval = xatou16(str_interval); 1125 if (interval > INT_MAX / 1000)
1126 interval = INT_MAX / 1000;
1125 } 1127 }
1126 if (col & OPT_n) { 1128 if (col & OPT_n) {
1127 if (str_iterations[0] == '-') 1129 if (str_iterations[0] == '-')
@@ -1169,7 +1171,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1169 /* We output to stdout, we need size of stdout (not stdin)! */ 1171 /* We output to stdout, we need size of stdout (not stdin)! */
1170 get_terminal_width_height(STDOUT_FILENO, &col, &G.lines); 1172 get_terminal_width_height(STDOUT_FILENO, &col, &G.lines);
1171 if (G.lines < 5 || col < 10) { 1173 if (G.lines < 5 || col < 10) {
1172 sleep(interval); 1174 sleep_for_duration(interval);
1173 continue; 1175 continue;
1174 } 1176 }
1175 if (col > LINE_BUF_SIZE - 2) 1177 if (col > LINE_BUF_SIZE - 2)
@@ -1254,7 +1256,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1254 break; 1256 break;
1255#if !ENABLE_FEATURE_TOP_INTERACTIVE 1257#if !ENABLE_FEATURE_TOP_INTERACTIVE
1256 clearmems(); 1258 clearmems();
1257 sleep(interval); 1259 sleep_for_duration(interval);
1258#else 1260#else
1259 new_mask = handle_input(scan_mask, interval); 1261 new_mask = handle_input(scan_mask, interval);
1260 if (new_mask == NO_RESCAN_MASK) 1262 if (new_mask == NO_RESCAN_MASK)
diff --git a/procps/uptime.c b/procps/uptime.c
index b0ee8391b..3262f41b4 100644
--- a/procps/uptime.c
+++ b/procps/uptime.c
@@ -45,7 +45,6 @@
45# include <sys/sysinfo.h> 45# include <sys/sysinfo.h>
46#endif 46#endif
47 47
48
49#ifndef FSHIFT 48#ifndef FSHIFT
50# define FSHIFT 16 /* nr of bits of precision */ 49# define FSHIFT 16 /* nr of bits of precision */
51#endif 50#endif
@@ -53,29 +52,48 @@
53#define LOAD_INT(x) (unsigned)((x) >> FSHIFT) 52#define LOAD_INT(x) (unsigned)((x) >> FSHIFT)
54#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1 - 1)) * 100) 53#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1 - 1)) * 100)
55 54
56
57int uptime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 55int uptime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
58int uptime_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 56int uptime_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
59{ 57{
60 unsigned updays, uphours, upminutes; 58 unsigned updays, uphours, upminutes;
59 unsigned opts;
61 struct sysinfo info; 60 struct sysinfo info;
62 struct tm *current_time; 61 struct tm *current_time;
63 time_t current_secs; 62 time_t current_secs;
64 63
64 opts = getopt32(argv, "s");
65
65 time(&current_secs); 66 time(&current_secs);
67 sysinfo(&info);
68
69 if (opts) // -s
70 current_secs -= info.uptime;
71
66 current_time = localtime(&current_secs); 72 current_time = localtime(&current_secs);
67 73
68 sysinfo(&info); 74 if (opts) { // -s
75 printf("%04u-%02u-%02u %02u:%02u:%02u\n",
76 current_time->tm_year + 1900, current_time->tm_mon + 1, current_time->tm_mday,
77 current_time->tm_hour, current_time->tm_min, current_time->tm_sec
78 );
79 /* The above way of calculating boot time is wobbly,
80 * info.uptime has only 1 second precision, which makes
81 * "uptime -s" wander +- one second.
82 * /proc/uptime may be better, it has 0.01s precision.
83 */
84 return EXIT_SUCCESS;
85 }
69 86
70 printf(" %02u:%02u:%02u up ", 87 printf(" %02u:%02u:%02u up ",
71 current_time->tm_hour, current_time->tm_min, current_time->tm_sec); 88 current_time->tm_hour, current_time->tm_min, current_time->tm_sec
89 );
72 updays = (unsigned) info.uptime / (unsigned)(60*60*24); 90 updays = (unsigned) info.uptime / (unsigned)(60*60*24);
73 if (updays) 91 if (updays != 0)
74 printf("%u day%s, ", updays, (updays != 1) ? "s" : ""); 92 printf("%u day%s, ", updays, (updays != 1) ? "s" : "");
75 upminutes = (unsigned) info.uptime / (unsigned)60; 93 upminutes = (unsigned) info.uptime / (unsigned)60;
76 uphours = (upminutes / (unsigned)60) % (unsigned)24; 94 uphours = (upminutes / (unsigned)60) % (unsigned)24;
77 upminutes %= 60; 95 upminutes %= 60;
78 if (uphours) 96 if (uphours != 0)
79 printf("%2u:%02u", uphours, upminutes); 97 printf("%2u:%02u", uphours, upminutes);
80 else 98 else
81 printf("%u min", upminutes); 99 printf("%u min", upminutes);
diff --git a/selinux/chcon.c b/selinux/chcon.c
index 3ddb2dd46..92eb76737 100644
--- a/selinux/chcon.c
+++ b/selinux/chcon.c
@@ -131,8 +131,10 @@ static int FAST_FUNC change_filedir_context(
131 bb_error_msg("can't change context of %s to %s", 131 bb_error_msg("can't change context of %s to %s",
132 fname, context_string); 132 fname, context_string);
133 } 133 }
134 } else if (option_mask32 & OPT_VERBOSE) { 134 } else {
135 printf("context of %s retained as %s\n", fname, context_string); 135 if (option_mask32 & OPT_VERBOSE) {
136 printf("context of %s retained as %s\n", fname, context_string);
137 }
136 rc = TRUE; 138 rc = TRUE;
137 } 139 }
138skip: 140skip:
@@ -202,7 +204,7 @@ int chcon_main(int argc UNUSED_PARAM, char **argv)
202 fname[fname_len] = '\0'; 204 fname[fname_len] = '\0';
203 205
204 if (recursive_action(fname, 206 if (recursive_action(fname,
205 1<<option_mask32 & OPT_RECURSIVE, 207 ((option_mask32 & OPT_RECURSIVE) ? ACTION_RECURSIVE : 0),
206 change_filedir_context, 208 change_filedir_context,
207 change_filedir_context, 209 change_filedir_context,
208 NULL, 0) != TRUE) 210 NULL, 0) != TRUE)
diff --git a/shell/ash.c b/shell/ash.c
index 4bd1c2c9d..7131609e4 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -6272,7 +6272,7 @@ static int substr_atoi(const char *s)
6272#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ 6272#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
6273#define EXP_VARTILDE2 0x20 /* expand tildes after colons only */ 6273#define EXP_VARTILDE2 0x20 /* expand tildes after colons only */
6274#define EXP_WORD 0x40 /* expand word in parameter expansion */ 6274#define EXP_WORD 0x40 /* expand word in parameter expansion */
6275#define EXP_QUOTED 0x80 /* expand word in double quotes */ 6275#define EXP_QUOTED 0x100 /* expand word in double quotes */
6276/* 6276/*
6277 * rmescape() flags 6277 * rmescape() flags
6278 */ 6278 */
@@ -6606,9 +6606,7 @@ memtodest(const char *p, size_t len, int syntax, int quotes)
6606 if (quotes & QUOTES_ESC) { 6606 if (quotes & QUOTES_ESC) {
6607 int n = SIT(c, syntax); 6607 int n = SIT(c, syntax);
6608 if (n == CCTL 6608 if (n == CCTL
6609 || (((quotes & EXP_FULL) || syntax != BASESYNTAX) 6609 || (syntax != BASESYNTAX && n == CBACK)
6610 && n == CBACK
6611 )
6612 ) { 6610 ) {
6613 USTPUTC(CTLESC, q); 6611 USTPUTC(CTLESC, q);
6614 } 6612 }
@@ -7236,8 +7234,15 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
7236 if (subtype == VSREPLACE || subtype == VSREPLACEALL) { 7234 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
7237 /* Find '/' and replace with NUL */ 7235 /* Find '/' and replace with NUL */
7238 repl = p; 7236 repl = p;
7237 /* The pattern can't be empty.
7238 * IOW: if the first char after "${v//" is a slash,
7239 * it does not terminate the pattern - it's the first char of the pattern:
7240 * v=/dev/ram; echo ${v////-} prints -dev-ram (pattern is "/")
7241 * v=/dev/ram; echo ${v///r/-} prints /dev-am (pattern is "/r")
7242 */
7243 if (*repl == '/')
7244 repl++;
7239 for (;;) { 7245 for (;;) {
7240 /* Handle escaped slashes, e.g. "${v/\//_}" (they are CTLESC'ed by this point) */
7241 if (*repl == '\0') { 7246 if (*repl == '\0') {
7242 repl = NULL; 7247 repl = NULL;
7243 break; 7248 break;
@@ -7246,6 +7251,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
7246 *repl = '\0'; 7251 *repl = '\0';
7247 break; 7252 break;
7248 } 7253 }
7254 /* Handle escaped slashes, e.g. "${v/\//_}" (they are CTLESC'ed by this point) */
7249 if ((unsigned char)*repl == CTLESC && repl[1]) 7255 if ((unsigned char)*repl == CTLESC && repl[1])
7250 repl++; 7256 repl++;
7251 repl++; 7257 repl++;
@@ -7549,14 +7555,13 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
7549 * ash -c 'echo ${#1#}' name:'1=#' 7555 * ash -c 'echo ${#1#}' name:'1=#'
7550 */ 7556 */
7551static NOINLINE ssize_t 7557static NOINLINE ssize_t
7552varvalue(char *name, int varflags, int flags, int *quotedp) 7558varvalue(char *name, int varflags, int flags, int quoted)
7553{ 7559{
7554 const char *p; 7560 const char *p;
7555 int num; 7561 int num;
7556 int i; 7562 int i;
7557 ssize_t len = 0; 7563 ssize_t len = 0;
7558 int sep; 7564 int sep;
7559 int quoted = *quotedp;
7560 int subtype = varflags & VSTYPE; 7565 int subtype = varflags & VSTYPE;
7561 int discard = subtype == VSPLUS || subtype == VSLENGTH; 7566 int discard = subtype == VSPLUS || subtype == VSLENGTH;
7562 int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; 7567 int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL;
@@ -7604,13 +7609,27 @@ varvalue(char *name, int varflags, int flags, int *quotedp)
7604 case '*': { 7609 case '*': {
7605 char **ap; 7610 char **ap;
7606 char sepc; 7611 char sepc;
7612 char c;
7607 7613
7608 if (quoted) 7614 /* We will set c to 0 or ~0 depending on whether
7609 sep = 0; 7615 * we're doing field splitting. We won't do field
7610 sep |= ifsset() ? ifsval()[0] : ' '; 7616 * splitting if either we're quoted or sep is zero.
7617 *
7618 * Instead of testing (quoted || !sep) the following
7619 * trick optimises away any branches by using the
7620 * fact that EXP_QUOTED (which is the only bit that
7621 * can be set in quoted) is the same as EXP_FULL <<
7622 * CHAR_BIT (which is the only bit that can be set
7623 * in sep).
7624 */
7625#if EXP_QUOTED >> CHAR_BIT != EXP_FULL
7626#error The following two lines expect EXP_QUOTED == EXP_FULL << CHAR_BIT
7627#endif
7628 c = !((quoted | ~sep) & EXP_QUOTED) - 1;
7629 sep &= ~quoted;
7630 sep |= ifsset() ? (unsigned char)(c & ifsval()[0]) : ' ';
7611 param: 7631 param:
7612 sepc = sep; 7632 sepc = sep;
7613 *quotedp = !sepc;
7614 ap = shellparam.p; 7633 ap = shellparam.p;
7615 if (!ap) 7634 if (!ap)
7616 return -1; 7635 return -1;
@@ -7675,7 +7694,6 @@ evalvar(char *p, int flag)
7675 char varflags; 7694 char varflags;
7676 char subtype; 7695 char subtype;
7677 int quoted; 7696 int quoted;
7678 char easy;
7679 char *var; 7697 char *var;
7680 int patloc; 7698 int patloc;
7681 int startloc; 7699 int startloc;
@@ -7689,12 +7707,11 @@ evalvar(char *p, int flag)
7689 7707
7690 quoted = flag & EXP_QUOTED; 7708 quoted = flag & EXP_QUOTED;
7691 var = p; 7709 var = p;
7692 easy = (!quoted || (*var == '@' && shellparam.nparam));
7693 startloc = expdest - (char *)stackblock(); 7710 startloc = expdest - (char *)stackblock();
7694 p = strchr(p, '=') + 1; //TODO: use var_end(p)? 7711 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
7695 7712
7696 again: 7713 again:
7697 varlen = varvalue(var, varflags, flag, &quoted); 7714 varlen = varvalue(var, varflags, flag, quoted);
7698 if (varflags & VSNUL) 7715 if (varflags & VSNUL)
7699 varlen--; 7716 varlen--;
7700 7717
@@ -7740,8 +7757,11 @@ evalvar(char *p, int flag)
7740 7757
7741 if (subtype == VSNORMAL) { 7758 if (subtype == VSNORMAL) {
7742 record: 7759 record:
7743 if (!easy) 7760 if (quoted) {
7744 goto end; 7761 quoted = *var == '@' && shellparam.nparam;
7762 if (!quoted)
7763 goto end;
7764 }
7745 recordregion(startloc, expdest - (char *)stackblock(), quoted); 7765 recordregion(startloc, expdest - (char *)stackblock(), quoted);
7746 goto end; 7766 goto end;
7747 } 7767 }
@@ -7999,7 +8019,7 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7999 } 8019 }
8000 } 8020 }
8001 } else { 8021 } else {
8002 if (*p == '\\') 8022 if (*p == '\\' && p[1])
8003 esc++; 8023 esc++;
8004 if (p[esc] == '/') { 8024 if (p[esc] == '/') {
8005 if (metaflag) 8025 if (metaflag)
@@ -8013,7 +8033,7 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
8013 return; 8033 return;
8014 p = name; 8034 p = name;
8015 do { 8035 do {
8016 if (*p == '\\') 8036 if (*p == '\\' && p[1])
8017 p++; 8037 p++;
8018 *enddir++ = *p; 8038 *enddir++ = *p;
8019 } while (*p++); 8039 } while (*p++);
@@ -8025,7 +8045,7 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
8025 if (name < start) { 8045 if (name < start) {
8026 p = name; 8046 p = name;
8027 do { 8047 do {
8028 if (*p == '\\') 8048 if (*p == '\\' && p[1])
8029 p++; 8049 p++;
8030 *enddir++ = *p++; 8050 *enddir++ = *p++;
8031 } while (p < start); 8051 } while (p < start);
@@ -8473,15 +8493,15 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
8473 8493
8474 /* Map to POSIX errors */ 8494 /* Map to POSIX errors */
8475 switch (e) { 8495 switch (e) {
8476 case EACCES: 8496 default:
8477 exerrno = 126; 8497 exerrno = 126;
8478 break; 8498 break;
8499 case ELOOP:
8500 case ENAMETOOLONG:
8479 case ENOENT: 8501 case ENOENT:
8502 case ENOTDIR:
8480 exerrno = 127; 8503 exerrno = 127;
8481 break; 8504 break;
8482 default:
8483 exerrno = 2;
8484 break;
8485 } 8505 }
8486 exitstatus = exerrno; 8506 exitstatus = exerrno;
8487 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n", 8507 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
@@ -10083,9 +10103,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
10083 shellparam.optind = 1; 10103 shellparam.optind = 1;
10084 shellparam.optoff = -1; 10104 shellparam.optoff = -1;
10085#endif 10105#endif
10086 pushlocalvars();
10087 evaltree(func->n.ndefun.body, flags & EV_TESTED); 10106 evaltree(func->n.ndefun.body, flags & EV_TESTED);
10088 poplocalvars(0);
10089 funcdone: 10107 funcdone:
10090 INT_OFF; 10108 INT_OFF;
10091 funcline = savefuncline; 10109 funcline = savefuncline;
@@ -10413,6 +10431,7 @@ find_builtin(const char *name)
10413/* 10431/*
10414 * Execute a simple command. 10432 * Execute a simple command.
10415 */ 10433 */
10434static void unwindfiles(struct parsefile *stop);
10416static int 10435static int
10417isassignment(const char *p) 10436isassignment(const char *p)
10418{ 10437{
@@ -10436,6 +10455,7 @@ evalcommand(union node *cmd, int flags)
10436 "\0\0", bltincmd /* why three NULs? */ 10455 "\0\0", bltincmd /* why three NULs? */
10437 }; 10456 };
10438 struct localvar_list *localvar_stop; 10457 struct localvar_list *localvar_stop;
10458 struct parsefile *file_stop;
10439 struct redirtab *redir_stop; 10459 struct redirtab *redir_stop;
10440 struct stackmark smark; 10460 struct stackmark smark;
10441 union node *argp; 10461 union node *argp;
@@ -10461,6 +10481,7 @@ evalcommand(union node *cmd, int flags)
10461 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); 10481 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
10462 setstackmark(&smark); 10482 setstackmark(&smark);
10463 localvar_stop = pushlocalvars(); 10483 localvar_stop = pushlocalvars();
10484 file_stop = g_parsefile;
10464 back_exitstatus = 0; 10485 back_exitstatus = 0;
10465 10486
10466 cmdentry.cmdtype = CMDBUILTIN; 10487 cmdentry.cmdtype = CMDBUILTIN;
@@ -10742,7 +10763,6 @@ evalcommand(union node *cmd, int flags)
10742 goto readstatus; 10763 goto readstatus;
10743 10764
10744 case CMDFUNCTION: 10765 case CMDFUNCTION:
10745 poplocalvars(1);
10746 /* See above for the rationale */ 10766 /* See above for the rationale */
10747 dowait(DOWAIT_NONBLOCK, NULL); 10767 dowait(DOWAIT_NONBLOCK, NULL);
10748 if (evalfun(cmdentry.u.func, argc, argv, flags)) 10768 if (evalfun(cmdentry.u.func, argc, argv, flags))
@@ -10756,6 +10776,7 @@ evalcommand(union node *cmd, int flags)
10756 if (cmd->ncmd.redirect) 10776 if (cmd->ncmd.redirect)
10757 popredir(/*drop:*/ cmd_is_exec); 10777 popredir(/*drop:*/ cmd_is_exec);
10758 unwindredir(redir_stop); 10778 unwindredir(redir_stop);
10779 unwindfiles(file_stop);
10759 unwindlocalvars(localvar_stop); 10780 unwindlocalvars(localvar_stop);
10760 if (lastarg) { 10781 if (lastarg) {
10761 /* dsl: I think this is intended to be used to support 10782 /* dsl: I think this is intended to be used to support
@@ -11278,14 +11299,20 @@ popfile(void)
11278 INT_ON; 11299 INT_ON;
11279} 11300}
11280 11301
11302static void
11303unwindfiles(struct parsefile *stop)
11304{
11305 while (g_parsefile != stop)
11306 popfile();
11307}
11308
11281/* 11309/*
11282 * Return to top level. 11310 * Return to top level.
11283 */ 11311 */
11284static void 11312static void
11285popallfiles(void) 11313popallfiles(void)
11286{ 11314{
11287 while (g_parsefile != &basepf) 11315 unwindfiles(&basepf);
11288 popfile();
11289} 11316}
11290 11317
11291#if !ENABLE_PLATFORM_MINGW32 11318#if !ENABLE_PLATFORM_MINGW32
@@ -12929,7 +12956,7 @@ parsesub: {
12929 STPUTC(c, out); 12956 STPUTC(c, out);
12930 c = pgetc_eatbnl(); 12957 c = pgetc_eatbnl();
12931 } while (isdigit(c)); 12958 } while (isdigit(c));
12932 } else { 12959 } else if (c != '}') {
12933 /* $[{[#]]<specialchar>[}] */ 12960 /* $[{[#]]<specialchar>[}] */
12934 int cc = c; 12961 int cc = c;
12935 12962
@@ -12955,7 +12982,8 @@ parsesub: {
12955 } 12982 }
12956 12983
12957 USTPUTC(cc, out); 12984 USTPUTC(cc, out);
12958 } 12985 } else
12986 goto badsub;
12959 12987
12960 if (c != '}' && subtype == VSLENGTH) { 12988 if (c != '}' && subtype == VSLENGTH) {
12961 /* ${#VAR didn't end with } */ 12989 /* ${#VAR didn't end with } */
@@ -14297,38 +14325,35 @@ letcmd(int argc UNUSED_PARAM, char **argv)
14297static int FAST_FUNC 14325static int FAST_FUNC
14298readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 14326readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14299{ 14327{
14300 char *opt_n = NULL; 14328 struct builtin_read_params params;
14301 char *opt_p = NULL;
14302 char *opt_t = NULL;
14303 char *opt_u = NULL;
14304 char *opt_d = NULL; /* optimized out if !BASH */
14305 int read_flags = 0;
14306 const char *r; 14329 const char *r;
14307 int i; 14330 int i;
14308 14331
14332 memset(&params, 0, sizeof(params));
14333
14309 while ((i = nextopt("p:u:rt:n:sd:")) != '\0') { 14334 while ((i = nextopt("p:u:rt:n:sd:")) != '\0') {
14310 switch (i) { 14335 switch (i) {
14311 case 'p': 14336 case 'p':
14312 opt_p = optionarg; 14337 params.opt_p = optionarg;
14313 break; 14338 break;
14314 case 'n': 14339 case 'n':
14315 opt_n = optionarg; 14340 params.opt_n = optionarg;
14316 break; 14341 break;
14317 case 's': 14342 case 's':
14318 read_flags |= BUILTIN_READ_SILENT; 14343 params.read_flags |= BUILTIN_READ_SILENT;
14319 break; 14344 break;
14320 case 't': 14345 case 't':
14321 opt_t = optionarg; 14346 params.opt_t = optionarg;
14322 break; 14347 break;
14323 case 'r': 14348 case 'r':
14324 read_flags |= BUILTIN_READ_RAW; 14349 params.read_flags |= BUILTIN_READ_RAW;
14325 break; 14350 break;
14326 case 'u': 14351 case 'u':
14327 opt_u = optionarg; 14352 params.opt_u = optionarg;
14328 break; 14353 break;
14329#if BASH_READ_D 14354#if BASH_READ_D
14330 case 'd': 14355 case 'd':
14331 opt_d = optionarg; 14356 params.opt_d = optionarg;
14332 break; 14357 break;
14333#endif 14358#endif
14334 default: 14359 default:
@@ -14336,21 +14361,16 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14336 } 14361 }
14337 } 14362 }
14338 14363
14364 params.argv = argptr;
14365 params.setvar = setvar0;
14366 params.ifs = bltinlookup("IFS"); /* can be NULL */
14367
14339 /* "read -s" needs to save/restore termios, can't allow ^C 14368 /* "read -s" needs to save/restore termios, can't allow ^C
14340 * to jump out of it. 14369 * to jump out of it.
14341 */ 14370 */
14342 again: 14371 again:
14343 INT_OFF; 14372 INT_OFF;
14344 r = shell_builtin_read(setvar0, 14373 r = shell_builtin_read(&params);
14345 argptr,
14346 bltinlookup("IFS"), /* can be NULL */
14347 read_flags,
14348 opt_n,
14349 opt_p,
14350 opt_t,
14351 opt_u,
14352 opt_d
14353 );
14354 INT_ON; 14374 INT_ON;
14355 14375
14356 if ((uintptr_t)r == 1 && errno == EINTR) { 14376 if ((uintptr_t)r == 1 && errno == EINTR) {
@@ -14595,6 +14615,7 @@ init(void)
14595 } 14615 }
14596 } 14616 }
14597 14617
14618 setvareq((char*)defifsvar, VTEXTFIXED);
14598 setvareq((char*)defoptindvar, VTEXTFIXED); 14619 setvareq((char*)defoptindvar, VTEXTFIXED);
14599 14620
14600 setvar0("PPID", utoa(getppid())); 14621 setvar0("PPID", utoa(getppid()));
diff --git a/shell/ash_test/ash-glob/glob_bkslash_in_var.right b/shell/ash_test/ash-glob/glob_bkslash_in_var.right
new file mode 100644
index 000000000..f1484b9e4
--- /dev/null
+++ b/shell/ash_test/ash-glob/glob_bkslash_in_var.right
@@ -0,0 +1,4 @@
1Unquoted non-matching glob in var:'test*.TMP/\name_doesnt_exist'
2Unquoted matching glob in var: 'testdir.TMP/name'
3Quoted non-matching glob in var: 'test*.TMP/\name_doesnt_exist'
4Quoted matching glob in var: 'test*.TMP/\name'
diff --git a/shell/ash_test/ash-glob/glob_bkslash_in_var.tests b/shell/ash_test/ash-glob/glob_bkslash_in_var.tests
new file mode 100755
index 000000000..e3dedc4ac
--- /dev/null
+++ b/shell/ash_test/ash-glob/glob_bkslash_in_var.tests
@@ -0,0 +1,10 @@
1mkdir testdir.TMP
2>testdir.TMP/name
3a="test*.TMP/\name_doesnt_exist"
4b="test*.TMP/\name"
5printf "Unquoted non-matching glob in var:'%s'\n" $a
6printf "Unquoted matching glob in var: '%s'\n" $b
7printf "Quoted non-matching glob in var: '%s'\n" "$a"
8printf "Quoted matching glob in var: '%s'\n" "$b"
9rm -f testdir.TMP/name
10rmdir testdir.TMP
diff --git a/shell/ash_test/ash-misc/empty_for1.right b/shell/ash_test/ash-misc/empty_for1.right
new file mode 100644
index 000000000..46ffcece7
--- /dev/null
+++ b/shell/ash_test/ash-misc/empty_for1.right
@@ -0,0 +1 @@
Zero:0
diff --git a/shell/ash_test/ash-misc/empty_for1.tests b/shell/ash_test/ash-misc/empty_for1.tests
new file mode 100755
index 000000000..5a2554d54
--- /dev/null
+++ b/shell/ash_test/ash-misc/empty_for1.tests
@@ -0,0 +1,5 @@
1false
2for v; do
3 exit 2
4done
5echo Zero:$?
diff --git a/shell/ash_test/ash-misc/env_and_func.right b/shell/ash_test/ash-misc/env_and_func.right
index 5fc3488ae..4a1545058 100644
--- a/shell/ash_test/ash-misc/env_and_func.right
+++ b/shell/ash_test/ash-misc/env_and_func.right
@@ -1,2 +1,2 @@
1var=val 1var=val
2var=val 2var=old
diff --git a/shell/ash_test/ash-misc/env_and_func.tests b/shell/ash_test/ash-misc/env_and_func.tests
index 3efef1a41..1c63eafd8 100755
--- a/shell/ash_test/ash-misc/env_and_func.tests
+++ b/shell/ash_test/ash-misc/env_and_func.tests
@@ -3,6 +3,6 @@ f() { echo "var=$var"; }
3# bash: POSIXLY_CORRECT behavior is to "leak" new variable values 3# bash: POSIXLY_CORRECT behavior is to "leak" new variable values
4# out of function invocations (similar to "special builtins" behavior); 4# out of function invocations (similar to "special builtins" behavior);
5# but in "bash mode", they don't leak. 5# but in "bash mode", they don't leak.
6# hush does not "leak" values. ash does. 6# hush does not "leak" values. ash used to, but now does not.
7var=val f 7var=val f
8echo "var=$var" 8echo "var=$var"
diff --git a/shell/ash_test/ash-vars/var_bash_pattern_starting_with_slash.right b/shell/ash_test/ash-vars/var_bash_pattern_starting_with_slash.right
new file mode 100644
index 000000000..439dca578
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_bash_pattern_starting_with_slash.right
@@ -0,0 +1,2 @@
1-dev-ram
2/dev-am
diff --git a/shell/ash_test/ash-vars/var_bash_pattern_starting_with_slash.tests b/shell/ash_test/ash-vars/var_bash_pattern_starting_with_slash.tests
new file mode 100755
index 000000000..b83fb8eeb
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_bash_pattern_starting_with_slash.tests
@@ -0,0 +1,3 @@
1v=/dev/ram
2echo ${v////-}
3echo ${v///r/-}
diff --git a/shell/ash_test/ash-vars/var_leak.right b/shell/ash_test/ash-vars/var_leak.right
index 01a5e3263..764680086 100644
--- a/shell/ash_test/ash-vars/var_leak.right
+++ b/shell/ash_test/ash-vars/var_leak.right
@@ -1,4 +1,4 @@
1should be empty: '' 1should be empty: ''
2should be empty: '' 2should be empty: ''
3should be not empty: 'val2' 3should be not empty: 'val2'
4should be not empty: 'val3' 4should be empty: ''
diff --git a/shell/ash_test/ash-vars/var_leak.tests b/shell/ash_test/ash-vars/var_leak.tests
index 5242e24eb..adf66692e 100755
--- a/shell/ash_test/ash-vars/var_leak.tests
+++ b/shell/ash_test/ash-vars/var_leak.tests
@@ -15,9 +15,7 @@ VAR=''
15VAR=val2 exec 2>&1 15VAR=val2 exec 2>&1
16echo "should be not empty: '$VAR'" 16echo "should be not empty: '$VAR'"
17 17
18# ash follows the "function call is a special builtin" rule here
19# (bash does not do it)
20f() { true; } 18f() { true; }
21VAR='' 19VAR=''
22VAR=val3 f 20VAR=val3 f
23echo "should be not empty: '$VAR'" 21echo "should be empty: '$VAR'"
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs5.right b/shell/ash_test/ash-vars/var_wordsplit_ifs5.right
new file mode 100644
index 000000000..46ffcece7
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_wordsplit_ifs5.right
@@ -0,0 +1 @@
Zero:0
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs5.tests b/shell/ash_test/ash-vars/var_wordsplit_ifs5.tests
new file mode 100755
index 000000000..d382116df
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_wordsplit_ifs5.tests
@@ -0,0 +1,4 @@
1IFS=
2set --
3set -- $@ $*
4echo Zero:$#
diff --git a/shell/hush.c b/shell/hush.c
index e3c6e2de9..881331c5b 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -990,7 +990,16 @@ struct globals {
990#if ENABLE_HUSH_MEMLEAK 990#if ENABLE_HUSH_MEMLEAK
991 unsigned long memleak_value; 991 unsigned long memleak_value;
992#endif 992#endif
993#if HUSH_DEBUG 993#if ENABLE_HUSH_MODE_X
994 unsigned x_mode_depth;
995 /* "set -x" output should not be redirectable with subsequent 2>FILE.
996 * We dup fd#2 to x_mode_fd when "set -x" is executed, and use it
997 * for all subsequent output.
998 */
999 int x_mode_fd;
1000 o_string x_mode_buf;
1001#endif
1002#if HUSH_DEBUG >= 2
994 int debug_indent; 1003 int debug_indent;
995#endif 1004#endif
996 struct sigaction sa; 1005 struct sigaction sa;
@@ -1212,7 +1221,7 @@ static const struct built_in_command bltins2[] = {
1212 1221
1213/* Debug printouts. 1222/* Debug printouts.
1214 */ 1223 */
1215#if HUSH_DEBUG 1224#if HUSH_DEBUG >= 2
1216/* prevent disasters with G.debug_indent < 0 */ 1225/* prevent disasters with G.debug_indent < 0 */
1217# define indent() fdprintf(2, "%*s", (G.debug_indent * 2) & 0xff, "") 1226# define indent() fdprintf(2, "%*s", (G.debug_indent * 2) & 0xff, "")
1218# define debug_enter() (G.debug_indent++) 1227# define debug_enter() (G.debug_indent++)
@@ -1657,6 +1666,12 @@ static int move_HFILEs_on_redirect(int fd, int avoid_fd)
1657 } 1666 }
1658 fl = fl->next_hfile; 1667 fl = fl->next_hfile;
1659 } 1668 }
1669#if ENABLE_HUSH_MODE_X
1670 if (G.x_mode_fd > 0 && fd == G.x_mode_fd) {
1671 G.x_mode_fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
1672 return 1; /* "found and moved" */
1673 }
1674#endif
1660 return 0; /* "not in the list" */ 1675 return 0; /* "not in the list" */
1661} 1676}
1662#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU 1677#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU
@@ -1668,9 +1683,15 @@ static void close_all_HFILE_list(void)
1668 * It is disastrous if we share memory with a vforked parent. 1683 * It is disastrous if we share memory with a vforked parent.
1669 * I'm not sure we never come here after vfork. 1684 * I'm not sure we never come here after vfork.
1670 * Therefore just close fd, nothing more. 1685 * Therefore just close fd, nothing more.
1686 *
1687 * ">" instead of ">=": we don't close fd#0,
1688 * interactive shell uses hfopen(NULL) as stdin input
1689 * which has fl->fd == 0, but fd#0 gets redirected in pipes.
1690 * If we'd close it here, then e.g. interactive "set | sort"
1691 * with NOFORKed sort, would have sort's input fd closed.
1671 */ 1692 */
1672 /*hfclose(fl); - unsafe */ 1693 if (fl->fd > 0)
1673 if (fl->fd >= 0) 1694 /*hfclose(fl); - unsafe */
1674 close(fl->fd); 1695 close(fl->fd);
1675 fl = fl->next_hfile; 1696 fl = fl->next_hfile;
1676 } 1697 }
@@ -2376,6 +2397,12 @@ static int set_local_var(char *str, unsigned flags)
2376 return 0; 2397 return 0;
2377} 2398}
2378 2399
2400static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
2401{
2402 char *var = xasprintf("%s=%s", name, val);
2403 set_local_var(var, /*flag:*/ 0);
2404}
2405
2379/* Used at startup and after each cd */ 2406/* Used at startup and after each cd */
2380static void set_pwd_var(unsigned flag) 2407static void set_pwd_var(unsigned flag)
2381{ 2408{
@@ -2422,15 +2449,6 @@ static int unset_local_var(const char *name)
2422} 2449}
2423#endif 2450#endif
2424 2451
2425#if BASH_HOSTNAME_VAR || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_READ || ENABLE_HUSH_GETOPTS \
2426 || (ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT)
2427static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
2428{
2429 char *var = xasprintf("%s=%s", name, val);
2430 set_local_var(var, /*flag:*/ 0);
2431}
2432#endif
2433
2434 2452
2435/* 2453/*
2436 * Helpers for "var1=val1 var2=val2 cmd" feature 2454 * Helpers for "var1=val1 var2=val2 cmd" feature
@@ -2900,6 +2918,11 @@ static void o_addstr(o_string *o, const char *str)
2900 o_addblock(o, str, strlen(str)); 2918 o_addblock(o, str, strlen(str));
2901} 2919}
2902 2920
2921static void o_addstr_with_NUL(o_string *o, const char *str)
2922{
2923 o_addblock(o, str, strlen(str) + 1);
2924}
2925
2903#if !BB_MMU 2926#if !BB_MMU
2904static void nommu_addchr(o_string *o, int ch) 2927static void nommu_addchr(o_string *o, int ch)
2905{ 2928{
@@ -2910,10 +2933,36 @@ static void nommu_addchr(o_string *o, int ch)
2910# define nommu_addchr(o, str) ((void)0) 2933# define nommu_addchr(o, str) ((void)0)
2911#endif 2934#endif
2912 2935
2913static void o_addstr_with_NUL(o_string *o, const char *str) 2936#if ENABLE_HUSH_MODE_X
2937static void x_mode_addchr(int ch)
2914{ 2938{
2915 o_addblock(o, str, strlen(str) + 1); 2939 o_addchr(&G.x_mode_buf, ch);
2940}
2941static void x_mode_addstr(const char *str)
2942{
2943 o_addstr(&G.x_mode_buf, str);
2916} 2944}
2945static void x_mode_addblock(const char *str, int len)
2946{
2947 o_addblock(&G.x_mode_buf, str, len);
2948}
2949static void x_mode_prefix(void)
2950{
2951 int n = G.x_mode_depth;
2952 do x_mode_addchr('+'); while (--n >= 0);
2953}
2954static void x_mode_flush(void)
2955{
2956 int len = G.x_mode_buf.length;
2957 if (len <= 0)
2958 return;
2959 if (G.x_mode_fd > 0) {
2960 G.x_mode_buf.data[len] = '\n';
2961 full_write(G.x_mode_fd, G.x_mode_buf.data, len + 1);
2962 }
2963 G.x_mode_buf.length = 0;
2964}
2965#endif
2917 2966
2918/* 2967/*
2919 * HUSH_BRACE_EXPANSION code needs corresponding quoting on variable expansion side. 2968 * HUSH_BRACE_EXPANSION code needs corresponding quoting on variable expansion side.
@@ -3068,6 +3117,13 @@ static int o_save_ptr_helper(o_string *o, int n)
3068 o->data = xrealloc(o->data, o->maxlen + 1); 3117 o->data = xrealloc(o->data, o->maxlen + 1);
3069 list = (char**)o->data; 3118 list = (char**)o->data;
3070 memmove(list + n + 0x10, list + n, string_len); 3119 memmove(list + n + 0x10, list + n, string_len);
3120 /*
3121 * expand_on_ifs() has a "previous argv[] ends in IFS?"
3122 * check. (grep for -prev-ifs-check-).
3123 * Ensure that argv[-1][last] is not garbage
3124 * but zero bytes, to save index check there.
3125 */
3126 list[n + 0x10 - 1] = 0;
3071 o->length += 0x10 * sizeof(list[0]); 3127 o->length += 0x10 * sizeof(list[0]);
3072 } else { 3128 } else {
3073 debug_printf_list("list[%d]=%d string_start=%d\n", 3129 debug_printf_list("list[%d]=%d string_start=%d\n",
@@ -3095,6 +3151,41 @@ static int o_get_last_ptr(o_string *o, int n)
3095 return ((int)(uintptr_t)list[n-1]) + string_start; 3151 return ((int)(uintptr_t)list[n-1]) + string_start;
3096} 3152}
3097 3153
3154/*
3155 * Globbing routines.
3156 *
3157 * Most words in commands need to be globbed, even ones which are
3158 * (single or double) quoted. This stems from the possiblity of
3159 * constructs like "abc"* and 'abc'* - these should be globbed.
3160 * Having a different code path for fully-quoted strings ("abc",
3161 * 'abc') would only help performance-wise, but we still need
3162 * code for partially-quoted strings.
3163 *
3164 * Unfortunately, if we want to match bash and ash behavior in all cases,
3165 * the logic can't be "shell-syntax argument is first transformed
3166 * to a string, then globbed, and if globbing does not match anything,
3167 * it is used verbatim". Here are two examples where it fails:
3168 *
3169 * echo 'b\*'?
3170 *
3171 * The globbing can't be avoided (because of '?' at the end).
3172 * The glob pattern is: b\\\*? - IOW, both \ and * are literals
3173 * and are glob-escaped. If this does not match, bash/ash print b\*?
3174 * - IOW: they "unbackslash" the glob pattern.
3175 * Now, look at this:
3176 *
3177 * v='\\\*'; echo b$v?
3178 *
3179 * The glob pattern is the same here: b\\\*? - the unquoted $v expansion
3180 * should be used as glob pattern with no changes. However, if glob
3181 * does not match, bash/ash print b\\\*? - NOT THE SAME as first example!
3182 *
3183 * ash implements this by having an encoded representation of the word
3184 * to glob, which IS NOT THE SAME as the glob pattern - it has more data.
3185 * Glob pattern is derived from it. If glob fails, the decision what result
3186 * should be is made using that encoded representation. Not glob pattern.
3187 */
3188
3098#if ENABLE_HUSH_BRACE_EXPANSION 3189#if ENABLE_HUSH_BRACE_EXPANSION
3099/* There in a GNU extension, GLOB_BRACE, but it is not usable: 3190/* There in a GNU extension, GLOB_BRACE, but it is not usable:
3100 * first, it processes even {a} (no commas), second, 3191 * first, it processes even {a} (no commas), second,
@@ -4877,6 +4968,15 @@ static int parse_dollar(o_string *as_string,
4877 end_ch = '}' * 0x100 + '/'; 4968 end_ch = '}' * 0x100 + '/';
4878 } 4969 }
4879 o_addchr(dest, ch); 4970 o_addchr(dest, ch);
4971 /* The pattern can't be empty.
4972 * IOW: if the first char after "${v//" is a slash,
4973 * it does not terminate the pattern - it's the first char of the pattern:
4974 * v=/dev/ram; echo ${v////-} prints -dev-ram (pattern is "/")
4975 * v=/dev/ram; echo ${v///r/-} prints /dev-am (pattern is "/r")
4976 */
4977 if (i_peek(input) == '/') {
4978 o_addchr(dest, i_getch(input));
4979 }
4880 again: 4980 again:
4881 if (!BB_MMU) 4981 if (!BB_MMU)
4882 pos = dest->length; 4982 pos = dest->length;
@@ -5796,12 +5896,16 @@ static int expand_on_ifs(o_string *output, int n, const char *str)
5796 /* Start new word... but not always! */ 5896 /* Start new word... but not always! */
5797 /* Case "v=' a'; echo ''$v": we do need to finalize empty word: */ 5897 /* Case "v=' a'; echo ''$v": we do need to finalize empty word: */
5798 if (output->has_quoted_part 5898 if (output->has_quoted_part
5799 /* Case "v=' a'; echo $v": 5899 /*
5900 * Case "v=' a'; echo $v":
5800 * here nothing precedes the space in $v expansion, 5901 * here nothing precedes the space in $v expansion,
5801 * therefore we should not finish the word 5902 * therefore we should not finish the word
5802 * (IOW: if there *is* word to finalize, only then do it): 5903 * (IOW: if there *is* word to finalize, only then do it):
5904 * It's okay if this accesses the byte before first argv[]:
5905 * past call to o_save_ptr() cleared it to zero byte
5906 * (grep for -prev-ifs-check-).
5803 */ 5907 */
5804 || (n > 0 && output->data[output->length - 1]) 5908 || output->data[output->length - 1]
5805 ) { 5909 ) {
5806 new_word: 5910 new_word:
5807 o_addchr(output, '\0'); 5911 o_addchr(output, '\0');
@@ -5856,6 +5960,26 @@ static char *encode_then_expand_string(const char *str)
5856 return exp_str; 5960 return exp_str;
5857} 5961}
5858 5962
5963static const char *first_special_char_in_vararg(const char *cp)
5964{
5965 for (;;) {
5966 if (!*cp) return NULL; /* string has no special chars */
5967 if (*cp == '$') return cp;
5968 if (*cp == '\\') return cp;
5969 if (*cp == '\'') return cp;
5970 if (*cp == '"') return cp;
5971#if ENABLE_HUSH_TICK
5972 if (*cp == '`') return cp;
5973#endif
5974 /* dquoted "${x:+ARG}" should not glob, therefore
5975 * '*' et al require some non-literal processing: */
5976 if (*cp == '*') return cp;
5977 if (*cp == '?') return cp;
5978 if (*cp == '[') return cp;
5979 cp++;
5980 }
5981}
5982
5859/* Expanding ARG in ${var#ARG}, ${var%ARG}, or ${var/ARG/ARG}. 5983/* Expanding ARG in ${var#ARG}, ${var%ARG}, or ${var/ARG/ARG}.
5860 * These can contain single- and double-quoted strings, 5984 * These can contain single- and double-quoted strings,
5861 * and treated as if the ARG string is initially unquoted. IOW: 5985 * and treated as if the ARG string is initially unquoted. IOW:
@@ -5875,19 +5999,10 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int
5875 char *exp_str; 5999 char *exp_str;
5876 struct in_str input; 6000 struct in_str input;
5877 o_string dest = NULL_O_STRING; 6001 o_string dest = NULL_O_STRING;
5878 const char *cp;
5879 6002
5880 cp = str; 6003 if (!first_special_char_in_vararg(str)) {
5881 for (;;) { 6004 /* string has no special chars */
5882 if (!*cp) return NULL; /* string has no special chars */ 6005 return NULL;
5883 if (*cp == '$') break;
5884 if (*cp == '\\') break;
5885 if (*cp == '\'') break;
5886 if (*cp == '"') break;
5887#if ENABLE_HUSH_TICK
5888 if (*cp == '`') break;
5889#endif
5890 cp++;
5891 } 6006 }
5892 6007
5893 setup_string_in_str(&input, str); 6008 setup_string_in_str(&input, str);
@@ -5968,26 +6083,19 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int
5968/* Expanding ARG in ${var+ARG}, ${var-ARG} 6083/* Expanding ARG in ${var+ARG}, ${var-ARG}
5969 */ 6084 */
5970static int encode_then_append_var_plusminus(o_string *output, int n, 6085static int encode_then_append_var_plusminus(o_string *output, int n,
5971 const char *str, int dquoted) 6086 char *str, int dquoted)
5972{ 6087{
5973 struct in_str input; 6088 struct in_str input;
5974 o_string dest = NULL_O_STRING; 6089 o_string dest = NULL_O_STRING;
5975 6090
5976#if 0 //todo? 6091 if (!first_special_char_in_vararg(str)
5977 const char *cp; 6092 && '\0' == str[strcspn(str, G.ifs)]
5978 cp = str; 6093 ) {
5979 for (;;) { 6094 /* string has no special chars
5980 if (!*cp) return NULL; /* string has no special chars */ 6095 * && string has no $IFS chars
5981 if (*cp == '$') break; 6096 */
5982 if (*cp == '\\') break; 6097 return expand_vars_to_list(output, n, str);
5983 if (*cp == '\'') break;
5984 if (*cp == '"') break;
5985#if ENABLE_HUSH_TICK
5986 if (*cp == '`') break;
5987#endif
5988 cp++;
5989 } 6098 }
5990#endif
5991 6099
5992 setup_string_in_str(&input, str); 6100 setup_string_in_str(&input, str);
5993 6101
@@ -7182,11 +7290,8 @@ static int generate_stream_from_string(const char *s, pid_t *pid_p)
7182 + (1 << SIGTTIN) 7290 + (1 << SIGTTIN)
7183 + (1 << SIGTTOU) 7291 + (1 << SIGTTOU)
7184 , SIG_IGN); 7292 , SIG_IGN);
7185 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
7186 close(channel[0]); /* NB: close _first_, then move fd! */ 7293 close(channel[0]); /* NB: close _first_, then move fd! */
7187 xmove_fd(channel[1], 1); 7294 xmove_fd(channel[1], 1);
7188 /* Prevent it from trying to handle ctrl-z etc */
7189 IF_HUSH_JOB(G.run_list_level = 1;)
7190# if ENABLE_HUSH_TRAP 7295# if ENABLE_HUSH_TRAP
7191 /* Awful hack for `trap` or $(trap). 7296 /* Awful hack for `trap` or $(trap).
7192 * 7297 *
@@ -7233,7 +7338,12 @@ static int generate_stream_from_string(const char *s, pid_t *pid_p)
7233 } 7338 }
7234# endif 7339# endif
7235# if BB_MMU 7340# if BB_MMU
7341 /* Prevent it from trying to handle ctrl-z etc */
7342 IF_HUSH_JOB(G.run_list_level = 1;)
7343 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
7236 reset_traps_to_defaults(); 7344 reset_traps_to_defaults();
7345 IF_HUSH_MODE_X(G.x_mode_depth++;)
7346 //bb_error_msg("%s: ++x_mode_depth=%d", __func__, G.x_mode_depth);
7237 parse_and_run_string(s); 7347 parse_and_run_string(s);
7238 _exit(G.last_exitcode); 7348 _exit(G.last_exitcode);
7239# else 7349# else
@@ -8014,28 +8124,65 @@ static void execvp_or_die(char **argv)
8014} 8124}
8015 8125
8016#if ENABLE_HUSH_MODE_X 8126#if ENABLE_HUSH_MODE_X
8127static void x_mode_print_optionally_squoted(const char *str)
8128{
8129 unsigned len;
8130 const char *cp;
8131
8132 cp = str;
8133
8134 /* the set of chars which-cause-string-to-be-squoted mimics bash */
8135 /* test a char with: bash -c 'set -x; echo "CH"' */
8136 if (str[strcspn(str, "\\\"'`$(){}[]<>;#&|~*?!^"
8137 " " "\001\002\003\004\005\006\007"
8138 "\010\011\012\013\014\015\016\017"
8139 "\020\021\022\023\024\025\026\027"
8140 "\030\031\032\033\034\035\036\037"
8141 )
8142 ] == '\0'
8143 ) {
8144 /* string has no special chars */
8145 x_mode_addstr(str);
8146 return;
8147 }
8148
8149 cp = str;
8150 for (;;) {
8151 /* print '....' up to EOL or first squote */
8152 len = (int)(strchrnul(cp, '\'') - cp);
8153 if (len != 0) {
8154 x_mode_addchr('\'');
8155 x_mode_addblock(cp, len);
8156 x_mode_addchr('\'');
8157 cp += len;
8158 }
8159 if (*cp == '\0')
8160 break;
8161 /* string contains squote(s), print them as \' */
8162 x_mode_addchr('\\');
8163 x_mode_addchr('\'');
8164 cp++;
8165 }
8166}
8017static void dump_cmd_in_x_mode(char **argv) 8167static void dump_cmd_in_x_mode(char **argv)
8018{ 8168{
8019 if (G_x_mode && argv) { 8169 if (G_x_mode && argv) {
8020 /* We want to output the line in one write op */ 8170 unsigned n;
8021 char *buf, *p;
8022 int len;
8023 int n;
8024 8171
8025 len = 3; 8172 /* "+[+++...][ cmd...]\n\0" */
8173 x_mode_prefix();
8026 n = 0; 8174 n = 0;
8027 while (argv[n]) 8175 while (argv[n]) {
8028 len += strlen(argv[n++]) + 1; 8176 x_mode_addchr(' ');
8029 buf = xmalloc(len); 8177 if (argv[n][0] == '\0') {
8030 buf[0] = '+'; 8178 x_mode_addchr('\'');
8031 p = buf + 1; 8179 x_mode_addchr('\'');
8032 n = 0; 8180 } else {
8033 while (argv[n]) 8181 x_mode_print_optionally_squoted(argv[n]);
8034 p += sprintf(p, " %s", argv[n++]); 8182 }
8035 *p++ = '\n'; 8183 n++;
8036 *p = '\0'; 8184 }
8037 fputs(buf, stderr); 8185 x_mode_flush();
8038 free(buf);
8039 } 8186 }
8040} 8187}
8041#else 8188#else
@@ -8821,16 +8968,25 @@ static NOINLINE int run_pipe(struct pipe *pi)
8821 restore_redirects(squirrel); 8968 restore_redirects(squirrel);
8822 8969
8823 /* Set shell variables */ 8970 /* Set shell variables */
8824 if (G_x_mode)
8825 bb_putchar_stderr('+');
8826 i = 0; 8971 i = 0;
8827 while (i < command->assignment_cnt) { 8972 while (i < command->assignment_cnt) {
8828 char *p = expand_string_to_string(argv[i], 8973 char *p = expand_string_to_string(argv[i],
8829 EXP_FLAG_ESC_GLOB_CHARS, 8974 EXP_FLAG_ESC_GLOB_CHARS,
8830 /*unbackslash:*/ 1 8975 /*unbackslash:*/ 1
8831 ); 8976 );
8832 if (G_x_mode) 8977#if ENABLE_HUSH_MODE_X
8833 fprintf(stderr, " %s", p); 8978 if (G_x_mode) {
8979 char *eq;
8980 if (i == 0)
8981 x_mode_prefix();
8982 x_mode_addchr(' ');
8983 eq = strchrnul(p, '=');
8984 if (*eq) eq++;
8985 x_mode_addblock(p, (eq - p));
8986 x_mode_print_optionally_squoted(eq);
8987 x_mode_flush();
8988 }
8989#endif
8834 debug_printf_env("set shell var:'%s'->'%s'\n", *argv, p); 8990 debug_printf_env("set shell var:'%s'->'%s'\n", *argv, p);
8835 if (set_local_var(p, /*flag:*/ 0)) { 8991 if (set_local_var(p, /*flag:*/ 0)) {
8836 /* assignment to readonly var / putenv error? */ 8992 /* assignment to readonly var / putenv error? */
@@ -8838,8 +8994,6 @@ static NOINLINE int run_pipe(struct pipe *pi)
8838 } 8994 }
8839 i++; 8995 i++;
8840 } 8996 }
8841 if (G_x_mode)
8842 bb_putchar_stderr('\n');
8843 /* Redirect error sets $? to 1. Otherwise, 8997 /* Redirect error sets $? to 1. Otherwise,
8844 * if evaluating assignment value set $?, retain it. 8998 * if evaluating assignment value set $?, retain it.
8845 * Else, clear $?: 8999 * Else, clear $?:
@@ -9288,11 +9442,11 @@ static int run_list(struct pipe *pi)
9288 }; /* argv list with one element: "$@" */ 9442 }; /* argv list with one element: "$@" */
9289 char **vals; 9443 char **vals;
9290 9444
9445 G.last_exitcode = rcode = EXIT_SUCCESS;
9291 vals = (char**)encoded_dollar_at_argv; 9446 vals = (char**)encoded_dollar_at_argv;
9292 if (pi->next->res_word == RES_IN) { 9447 if (pi->next->res_word == RES_IN) {
9293 /* if no variable values after "in" we skip "for" */ 9448 /* if no variable values after "in" we skip "for" */
9294 if (!pi->next->cmds[0].argv) { 9449 if (!pi->next->cmds[0].argv) {
9295 G.last_exitcode = rcode = EXIT_SUCCESS;
9296 debug_printf_exec(": null FOR: exitcode EXIT_SUCCESS\n"); 9450 debug_printf_exec(": null FOR: exitcode EXIT_SUCCESS\n");
9297 break; 9451 break;
9298 } 9452 }
@@ -9626,6 +9780,7 @@ static int set_mode(int state, char mode, const char *o_opt)
9626 break; 9780 break;
9627 case 'x': 9781 case 'x':
9628 IF_HUSH_MODE_X(G_x_mode = state;) 9782 IF_HUSH_MODE_X(G_x_mode = state;)
9783 IF_HUSH_MODE_X(if (G.x_mode_fd <= 0) G.x_mode_fd = dup_CLOEXEC(2, 10);)
9629 break; 9784 break;
9630 case 'o': 9785 case 'o':
9631 if (!o_opt) { 9786 if (!o_opt) {
@@ -9731,6 +9886,10 @@ int hush_main(int argc, char **argv)
9731 uname(&uts); 9886 uname(&uts);
9732 set_local_var_from_halves("HOSTNAME", uts.nodename); 9887 set_local_var_from_halves("HOSTNAME", uts.nodename);
9733 } 9888 }
9889#endif
9890 /* IFS is not inherited from the parent environment */
9891 set_local_var_from_halves("IFS", defifs);
9892
9734 /* bash also exports SHLVL and _, 9893 /* bash also exports SHLVL and _,
9735 * and sets (but doesn't export) the following variables: 9894 * and sets (but doesn't export) the following variables:
9736 * BASH=/bin/bash 9895 * BASH=/bin/bash
@@ -9761,10 +9920,8 @@ int hush_main(int argc, char **argv)
9761 * TERM=dumb 9920 * TERM=dumb
9762 * OPTERR=1 9921 * OPTERR=1
9763 * OPTIND=1 9922 * OPTIND=1
9764 * IFS=$' \t\n'
9765 * PS4='+ ' 9923 * PS4='+ '
9766 */ 9924 */
9767#endif
9768 9925
9769#if ENABLE_HUSH_LINENO_VAR 9926#if ENABLE_HUSH_LINENO_VAR
9770 if (ENABLE_HUSH_LINENO_VAR) { 9927 if (ENABLE_HUSH_LINENO_VAR) {
@@ -10226,6 +10383,8 @@ static int FAST_FUNC builtin_eval(char **argv)
10226 if (!argv[0]) 10383 if (!argv[0])
10227 return EXIT_SUCCESS; 10384 return EXIT_SUCCESS;
10228 10385
10386 IF_HUSH_MODE_X(G.x_mode_depth++;)
10387 //bb_error_msg("%s: ++x_mode_depth=%d", __func__, G.x_mode_depth);
10229 if (!argv[1]) { 10388 if (!argv[1]) {
10230 /* bash: 10389 /* bash:
10231 * eval "echo Hi; done" ("done" is syntax error): 10390 * eval "echo Hi; done" ("done" is syntax error):
@@ -10255,6 +10414,8 @@ static int FAST_FUNC builtin_eval(char **argv)
10255 parse_and_run_string(str); 10414 parse_and_run_string(str);
10256 free(str); 10415 free(str);
10257 } 10416 }
10417 IF_HUSH_MODE_X(G.x_mode_depth--;)
10418 //bb_error_msg("%s: --x_mode_depth=%d", __func__, G.x_mode_depth);
10258 return G.last_exitcode; 10419 return G.last_exitcode;
10259} 10420}
10260 10421
@@ -10374,40 +10535,29 @@ static int FAST_FUNC builtin_type(char **argv)
10374static int FAST_FUNC builtin_read(char **argv) 10535static int FAST_FUNC builtin_read(char **argv)
10375{ 10536{
10376 const char *r; 10537 const char *r;
10377 char *opt_n = NULL; 10538 struct builtin_read_params params;
10378 char *opt_p = NULL; 10539
10379 char *opt_t = NULL; 10540 memset(&params, 0, sizeof(params));
10380 char *opt_u = NULL;
10381 char *opt_d = NULL; /* optimized out if !BASH */
10382 const char *ifs;
10383 int read_flags;
10384 10541
10385 /* "!": do not abort on errors. 10542 /* "!": do not abort on errors.
10386 * Option string must start with "sr" to match BUILTIN_READ_xxx 10543 * Option string must start with "sr" to match BUILTIN_READ_xxx
10387 */ 10544 */
10388 read_flags = getopt32(argv, 10545 params.read_flags = getopt32(argv,
10389#if BASH_READ_D 10546#if BASH_READ_D
10390 "!srn:p:t:u:d:", &opt_n, &opt_p, &opt_t, &opt_u, &opt_d 10547 "!srn:p:t:u:d:", &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u, &params.opt_d
10391#else 10548#else
10392 "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u 10549 "!srn:p:t:u:", &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u
10393#endif 10550#endif
10394 ); 10551 );
10395 if (read_flags == (uint32_t)-1) 10552 if ((uint32_t)params.read_flags == (uint32_t)-1)
10396 return EXIT_FAILURE; 10553 return EXIT_FAILURE;
10397 argv += optind; 10554 argv += optind;
10398 ifs = get_local_var_value("IFS"); /* can be NULL */ 10555 params.argv = argv;
10556 params.setvar = set_local_var_from_halves;
10557 params.ifs = get_local_var_value("IFS"); /* can be NULL */
10399 10558
10400 again: 10559 again:
10401 r = shell_builtin_read(set_local_var_from_halves, 10560 r = shell_builtin_read(&params);
10402 argv,
10403 ifs,
10404 read_flags,
10405 opt_n,
10406 opt_p,
10407 opt_t,
10408 opt_u,
10409 opt_d
10410 );
10411 10561
10412 if ((uintptr_t)r == 1 && errno == EINTR) { 10562 if ((uintptr_t)r == 1 && errno == EINTR) {
10413 unsigned sig = check_and_run_traps(); 10563 unsigned sig = check_and_run_traps();
diff --git a/shell/hush_test/hush-glob/glob_bkslash_in_var.right b/shell/hush_test/hush-glob/glob_bkslash_in_var.right
new file mode 100644
index 000000000..f1484b9e4
--- /dev/null
+++ b/shell/hush_test/hush-glob/glob_bkslash_in_var.right
@@ -0,0 +1,4 @@
1Unquoted non-matching glob in var:'test*.TMP/\name_doesnt_exist'
2Unquoted matching glob in var: 'testdir.TMP/name'
3Quoted non-matching glob in var: 'test*.TMP/\name_doesnt_exist'
4Quoted matching glob in var: 'test*.TMP/\name'
diff --git a/shell/hush_test/hush-glob/glob_bkslash_in_var.tests b/shell/hush_test/hush-glob/glob_bkslash_in_var.tests
new file mode 100755
index 000000000..e3dedc4ac
--- /dev/null
+++ b/shell/hush_test/hush-glob/glob_bkslash_in_var.tests
@@ -0,0 +1,10 @@
1mkdir testdir.TMP
2>testdir.TMP/name
3a="test*.TMP/\name_doesnt_exist"
4b="test*.TMP/\name"
5printf "Unquoted non-matching glob in var:'%s'\n" $a
6printf "Unquoted matching glob in var: '%s'\n" $b
7printf "Quoted non-matching glob in var: '%s'\n" "$a"
8printf "Quoted matching glob in var: '%s'\n" "$b"
9rm -f testdir.TMP/name
10rmdir testdir.TMP
diff --git a/shell/hush_test/hush-misc/empty_for1.right b/shell/hush_test/hush-misc/empty_for1.right
new file mode 100644
index 000000000..46ffcece7
--- /dev/null
+++ b/shell/hush_test/hush-misc/empty_for1.right
@@ -0,0 +1 @@
Zero:0
diff --git a/shell/hush_test/hush-misc/empty_for1.tests b/shell/hush_test/hush-misc/empty_for1.tests
new file mode 100755
index 000000000..5a2554d54
--- /dev/null
+++ b/shell/hush_test/hush-misc/empty_for1.tests
@@ -0,0 +1,5 @@
1false
2for v; do
3 exit 2
4done
5echo Zero:$?
diff --git a/shell/hush_test/hush-misc/env_and_func.tests b/shell/hush_test/hush-misc/env_and_func.tests
index 3efef1a41..1c63eafd8 100755
--- a/shell/hush_test/hush-misc/env_and_func.tests
+++ b/shell/hush_test/hush-misc/env_and_func.tests
@@ -3,6 +3,6 @@ f() { echo "var=$var"; }
3# bash: POSIXLY_CORRECT behavior is to "leak" new variable values 3# bash: POSIXLY_CORRECT behavior is to "leak" new variable values
4# out of function invocations (similar to "special builtins" behavior); 4# out of function invocations (similar to "special builtins" behavior);
5# but in "bash mode", they don't leak. 5# but in "bash mode", they don't leak.
6# hush does not "leak" values. ash does. 6# hush does not "leak" values. ash used to, but now does not.
7var=val f 7var=val f
8echo "var=$var" 8echo "var=$var"
diff --git a/shell/hush_test/hush-quoting/quote_in_varexp1.right b/shell/hush_test/hush-quoting/quote_in_varexp1.right
new file mode 100644
index 000000000..99a0aea7c
--- /dev/null
+++ b/shell/hush_test/hush-quoting/quote_in_varexp1.right
@@ -0,0 +1,2 @@
1''
2Ok:0
diff --git a/shell/hush_test/hush-quoting/quote_in_varexp1.tests b/shell/hush_test/hush-quoting/quote_in_varexp1.tests
new file mode 100755
index 000000000..1b97b0556
--- /dev/null
+++ b/shell/hush_test/hush-quoting/quote_in_varexp1.tests
@@ -0,0 +1,2 @@
1x="''''"; echo "${x#"${x+''}"''}"
2echo Ok:$?
diff --git a/shell/hush_test/hush-vars/var_bash_pattern_starting_with_slash.right b/shell/hush_test/hush-vars/var_bash_pattern_starting_with_slash.right
new file mode 100644
index 000000000..439dca578
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash_pattern_starting_with_slash.right
@@ -0,0 +1,2 @@
1-dev-ram
2/dev-am
diff --git a/shell/hush_test/hush-vars/var_bash_pattern_starting_with_slash.tests b/shell/hush_test/hush-vars/var_bash_pattern_starting_with_slash.tests
new file mode 100755
index 000000000..b83fb8eeb
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash_pattern_starting_with_slash.tests
@@ -0,0 +1,3 @@
1v=/dev/ram
2echo ${v////-}
3echo ${v///r/-}
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs5.right b/shell/hush_test/hush-vars/var_wordsplit_ifs5.right
new file mode 100644
index 000000000..46ffcece7
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_wordsplit_ifs5.right
@@ -0,0 +1 @@
Zero:0
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs5.tests b/shell/hush_test/hush-vars/var_wordsplit_ifs5.tests
new file mode 100755
index 000000000..d382116df
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_wordsplit_ifs5.tests
@@ -0,0 +1,4 @@
1IFS=
2set --
3set -- $@ $*
4echo Zero:$#
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 82102778c..23e5f1c25 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -50,16 +50,7 @@ int FAST_FUNC is_well_formed_var_name(const char *s, char terminator)
50//Here we can simply store "VAR=" at buffer start and store read data directly 50//Here we can simply store "VAR=" at buffer start and store read data directly
51//after "=", then pass buffer to setvar() to consume. 51//after "=", then pass buffer to setvar() to consume.
52const char* FAST_FUNC 52const char* FAST_FUNC
53shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), 53shell_builtin_read(struct builtin_read_params *params)
54 char **argv,
55 const char *ifs,
56 int read_flags,
57 const char *opt_n,
58 const char *opt_p,
59 const char *opt_t,
60 const char *opt_u,
61 const char *opt_d
62)
63{ 54{
64 struct pollfd pfd[1]; 55 struct pollfd pfd[1];
65#define fd (pfd[0].fd) /* -u FD */ 56#define fd (pfd[0].fd) /* -u FD */
@@ -76,9 +67,13 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
76 int bufpos; /* need to be able to hold -1 */ 67 int bufpos; /* need to be able to hold -1 */
77 int startword; 68 int startword;
78 smallint backslash; 69 smallint backslash;
70 char **argv;
71 const char *ifs;
72 int read_flags;
79 73
80 errno = err = 0; 74 errno = err = 0;
81 75
76 argv = params->argv;
82 pp = argv; 77 pp = argv;
83 while (*pp) { 78 while (*pp) {
84 if (!is_well_formed_var_name(*pp, '\0')) { 79 if (!is_well_formed_var_name(*pp, '\0')) {
@@ -90,29 +85,29 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
90 } 85 }
91 86
92 nchars = 0; /* if != 0, -n is in effect */ 87 nchars = 0; /* if != 0, -n is in effect */
93 if (opt_n) { 88 if (params->opt_n) {
94 nchars = bb_strtou(opt_n, NULL, 10); 89 nchars = bb_strtou(params->opt_n, NULL, 10);
95 if (nchars < 0 || errno) 90 if (nchars < 0 || errno)
96 return "invalid count"; 91 return "invalid count";
97 /* note: "-n 0": off (bash 3.2 does this too) */ 92 /* note: "-n 0": off (bash 3.2 does this too) */
98 } 93 }
99 94
100 end_ms = 0; 95 end_ms = 0;
101 if (opt_t && !ENABLE_FEATURE_SH_READ_FRAC) { 96 if (params->opt_t && !ENABLE_FEATURE_SH_READ_FRAC) {
102 end_ms = bb_strtou(opt_t, NULL, 10); 97 end_ms = bb_strtou(params->opt_t, NULL, 10);
103 if (errno) 98 if (errno)
104 return "invalid timeout"; 99 return "invalid timeout";
105 if (end_ms > UINT_MAX / 2048) /* be safely away from overflow */ 100 if (end_ms > UINT_MAX / 2048) /* be safely away from overflow */
106 end_ms = UINT_MAX / 2048; 101 end_ms = UINT_MAX / 2048;
107 end_ms *= 1000; 102 end_ms *= 1000;
108 } 103 }
109 if (opt_t && ENABLE_FEATURE_SH_READ_FRAC) { 104 if (params->opt_t && ENABLE_FEATURE_SH_READ_FRAC) {
110 /* bash 4.3 (maybe earlier) supports -t N.NNNNNN */ 105 /* bash 4.3 (maybe earlier) supports -t N.NNNNNN */
111 char *p; 106 char *p;
112 /* Eat up to three fractional digits */ 107 /* Eat up to three fractional digits */
113 int frac_digits = 3 + 1; 108 int frac_digits = 3 + 1;
114 109
115 end_ms = bb_strtou(opt_t, &p, 10); 110 end_ms = bb_strtou(params->opt_t, &p, 10);
116 if (end_ms > UINT_MAX / 2048) /* be safely away from overflow */ 111 if (end_ms > UINT_MAX / 2048) /* be safely away from overflow */
117 end_ms = UINT_MAX / 2048; 112 end_ms = UINT_MAX / 2048;
118 113
@@ -134,13 +129,13 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
134 } 129 }
135 130
136 fd = STDIN_FILENO; 131 fd = STDIN_FILENO;
137 if (opt_u) { 132 if (params->opt_u) {
138 fd = bb_strtou(opt_u, NULL, 10); 133 fd = bb_strtou(params->opt_u, NULL, 10);
139 if (fd < 0 || errno) 134 if (fd < 0 || errno)
140 return "invalid file descriptor"; 135 return "invalid file descriptor";
141 } 136 }
142 137
143 if (opt_t && end_ms == 0) { 138 if (params->opt_t && end_ms == 0) {
144#if !ENABLE_PLATFORM_MINGW32 139#if !ENABLE_PLATFORM_MINGW32
145 /* "If timeout is 0, read returns immediately, without trying 140 /* "If timeout is 0, read returns immediately, without trying
146 * to read any data. The exit status is 0 if input is available 141 * to read any data. The exit status is 0 if input is available
@@ -157,14 +152,16 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
157#endif 152#endif
158 } 153 }
159 154
160 if (opt_p && isatty(fd)) { 155 if (params->opt_p && isatty(fd)) {
161 fputs(opt_p, stderr); 156 fputs(params->opt_p, stderr);
162 fflush_all(); 157 fflush_all();
163 } 158 }
164 159
160 ifs = params->ifs;
165 if (ifs == NULL) 161 if (ifs == NULL)
166 ifs = defifs; 162 ifs = defifs;
167 163
164 read_flags = params->read_flags;
168#if !ENABLE_PLATFORM_MINGW32 165#if !ENABLE_PLATFORM_MINGW32
169 if (nchars || (read_flags & BUILTIN_READ_SILENT)) { 166 if (nchars || (read_flags & BUILTIN_READ_SILENT)) {
170 tcgetattr(fd, &tty); 167 tcgetattr(fd, &tty);
@@ -193,11 +190,11 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
193 retval = (const char *)(uintptr_t)0; 190 retval = (const char *)(uintptr_t)0;
194 startword = 1; 191 startword = 1;
195 backslash = 0; 192 backslash = 0;
196 if (opt_t) 193 if (params->opt_t)
197 end_ms += (unsigned)monotonic_ms(); 194 end_ms += (unsigned)monotonic_ms();
198 buffer = NULL; 195 buffer = NULL;
199 bufpos = 0; 196 bufpos = 0;
200 delim = opt_d ? *opt_d : '\n'; 197 delim = params->opt_d ? params->opt_d[0] : '\n';
201 do { 198 do {
202 char c; 199 char c;
203 int timeout; 200 int timeout;
@@ -206,7 +203,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
206 buffer = xrealloc(buffer, bufpos + 0x101); 203 buffer = xrealloc(buffer, bufpos + 0x101);
207 204
208 timeout = -1; 205 timeout = -1;
209 if (opt_t) { 206 if (params->opt_t) {
210 timeout = end_ms - (unsigned)monotonic_ms(); 207 timeout = end_ms - (unsigned)monotonic_ms();
211 /* ^^^^^^^^^^^^^ all values are unsigned, 208 /* ^^^^^^^^^^^^^ all values are unsigned,
212 * wrapping math is used here, good even if 209 * wrapping math is used here, good even if
@@ -238,7 +235,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
238 } 235 }
239#else 236#else
240 errno = 0; 237 errno = 0;
241 if (isatty(fd) && (opt_n || opt_d || opt_t || 238 if (isatty(fd) && (params->opt_n || params->opt_d || params->opt_t ||
242 (read_flags & BUILTIN_READ_SILENT))) { 239 (read_flags & BUILTIN_READ_SILENT))) {
243 int64_t key; 240 int64_t key;
244 241
@@ -285,7 +282,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
285 * without variable names (bash compat). 282 * without variable names (bash compat).
286 * Thus, "read" and "read REPLY" are not the same. 283 * Thus, "read" and "read REPLY" are not the same.
287 */ 284 */
288 if (!opt_d && argv[0]) { 285 if (!params->opt_d && argv[0]) {
289/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */ 286/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */
290 const char *is_ifs = strchr(ifs, c); 287 const char *is_ifs = strchr(ifs, c);
291 if (startword && is_ifs) { 288 if (startword && is_ifs) {
@@ -300,7 +297,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
300 if (argv[1] != NULL && is_ifs) { 297 if (argv[1] != NULL && is_ifs) {
301 buffer[bufpos] = '\0'; 298 buffer[bufpos] = '\0';
302 bufpos = 0; 299 bufpos = 0;
303 setvar(*argv, buffer); 300 params->setvar(*argv, buffer);
304 argv++; 301 argv++;
305 /* can we skip one non-space ifs char? (2: yes) */ 302 /* can we skip one non-space ifs char? (2: yes) */
306 startword = isspace(c) ? 2 : 1; 303 startword = isspace(c) ? 2 : 1;
@@ -352,14 +349,14 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
352 } 349 }
353 350
354 /* Use the remainder as a value for the next variable */ 351 /* Use the remainder as a value for the next variable */
355 setvar(*argv, buffer); 352 params->setvar(*argv, buffer);
356 /* Set the rest to "" */ 353 /* Set the rest to "" */
357 while (*++argv) 354 while (*++argv)
358 setvar(*argv, ""); 355 params->setvar(*argv, "");
359 } else { 356 } else {
360 /* Note: no $IFS removal */ 357 /* Note: no $IFS removal */
361 buffer[bufpos] = '\0'; 358 buffer[bufpos] = '\0';
362 setvar("REPLY", buffer); 359 params->setvar("REPLY", buffer);
363 } 360 }
364 361
365 ret: 362 ret:
diff --git a/shell/shell_common.h b/shell/shell_common.h
index 875fd9ea7..a1323021d 100644
--- a/shell/shell_common.h
+++ b/shell/shell_common.h
@@ -30,6 +30,17 @@ int FAST_FUNC is_well_formed_var_name(const char *s, char terminator);
30 30
31/* Builtins */ 31/* Builtins */
32 32
33struct builtin_read_params {
34 int read_flags;
35 void FAST_FUNC (*setvar)(const char *name, const char *val);
36 char **argv;
37 const char *ifs;
38 const char *opt_n;
39 const char *opt_p;
40 const char *opt_t;
41 const char *opt_u;
42 const char *opt_d;
43};
33enum { 44enum {
34 BUILTIN_READ_SILENT = 1 << 0, 45 BUILTIN_READ_SILENT = 1 << 0,
35 BUILTIN_READ_RAW = 1 << 1, 46 BUILTIN_READ_RAW = 1 << 1,
@@ -40,16 +51,7 @@ enum {
40// shell_builtin_read(setvar,argv,ifs,read_flags) 51// shell_builtin_read(setvar,argv,ifs,read_flags)
41//#endif 52//#endif
42const char* FAST_FUNC 53const char* FAST_FUNC
43shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), 54shell_builtin_read(struct builtin_read_params *params);
44 char **argv,
45 const char *ifs,
46 int read_flags,
47 const char *opt_n,
48 const char *opt_p,
49 const char *opt_t,
50 const char *opt_u,
51 const char *opt_d
52);
53 55
54int FAST_FUNC 56int FAST_FUNC
55shell_builtin_ulimit(char **argv); 57shell_builtin_ulimit(char **argv);
diff --git a/sysklogd/klogd.c b/sysklogd/klogd.c
index 728cc8d38..25ddf3172 100644
--- a/sysklogd/klogd.c
+++ b/sysklogd/klogd.c
@@ -53,7 +53,7 @@
53//usage:#define klogd_trivial_usage 53//usage:#define klogd_trivial_usage
54//usage: "[-c N] [-n]" 54//usage: "[-c N] [-n]"
55//usage:#define klogd_full_usage "\n\n" 55//usage:#define klogd_full_usage "\n\n"
56//usage: "Kernel logger\n" 56//usage: "Log kernel messages to syslog\n"
57//usage: "\n -c N Print to console messages more urgent than prio N (1-8)" 57//usage: "\n -c N Print to console messages more urgent than prio N (1-8)"
58//usage: "\n -n Run in foreground" 58//usage: "\n -n Run in foreground"
59 59
@@ -85,6 +85,7 @@ static void klogd_setloglevel(int lvl)
85 85
86static int klogd_read(char *bufp, int len) 86static int klogd_read(char *bufp, int len)
87{ 87{
88 /* "2 -- Read from the log." */
88 return klogctl(2, bufp, len); 89 return klogctl(2, bufp, len);
89} 90}
90# define READ_ERROR "klogctl(2) error" 91# define READ_ERROR "klogctl(2) error"
@@ -238,7 +239,6 @@ int klogd_main(int argc UNUSED_PARAM, char **argv)
238 int priority; 239 int priority;
239 char *start; 240 char *start;
240 241
241 /* "2 -- Read from the log." */
242 start = log_buffer + used; 242 start = log_buffer + used;
243 n = klogd_read(start, KLOGD_LOGBUF_SIZE-1 - used); 243 n = klogd_read(start, KLOGD_LOGBUF_SIZE-1 - used);
244 if (n < 0) { 244 if (n < 0) {
@@ -275,10 +275,13 @@ int klogd_main(int argc UNUSED_PARAM, char **argv)
275 priority = LOG_INFO; 275 priority = LOG_INFO;
276 if (*start == '<') { 276 if (*start == '<') {
277 start++; 277 start++;
278 if (*start) 278 if (*start) {
279 priority = strtoul(start, &start, 10); 279 char *end;
280 if (*start == '>') 280 priority = strtoul(start, &end, 10);
281 start++; 281 if (*end == '>')
282 end++;
283 start = end;
284 }
282 } 285 }
283 /* Log (only non-empty lines) */ 286 /* Log (only non-empty lines) */
284 if (*start) 287 if (*start)
diff --git a/sysklogd/syslogd.c b/sysklogd/syslogd.c
index 4265f4f90..5630d97fc 100644
--- a/sysklogd/syslogd.c
+++ b/sysklogd/syslogd.c
@@ -140,6 +140,7 @@
140//usage: ) 140//usage: )
141//usage: "\n -l N Log only messages more urgent than prio N (1-8)" 141//usage: "\n -l N Log only messages more urgent than prio N (1-8)"
142//usage: "\n -S Smaller output" 142//usage: "\n -S Smaller output"
143//usage: "\n -t Strip client-generated timestamps"
143//usage: IF_FEATURE_SYSLOGD_DUP( 144//usage: IF_FEATURE_SYSLOGD_DUP(
144//usage: "\n -D Drop duplicates" 145//usage: "\n -D Drop duplicates"
145//usage: ) 146//usage: )
@@ -316,6 +317,7 @@ enum {
316 OPTBIT_outfile, // -O 317 OPTBIT_outfile, // -O
317 OPTBIT_loglevel, // -l 318 OPTBIT_loglevel, // -l
318 OPTBIT_small, // -S 319 OPTBIT_small, // -S
320 OPTBIT_timestamp, // -t
319 IF_FEATURE_ROTATE_LOGFILE(OPTBIT_filesize ,) // -s 321 IF_FEATURE_ROTATE_LOGFILE(OPTBIT_filesize ,) // -s
320 IF_FEATURE_ROTATE_LOGFILE(OPTBIT_rotatecnt ,) // -b 322 IF_FEATURE_ROTATE_LOGFILE(OPTBIT_rotatecnt ,) // -b
321 IF_FEATURE_REMOTE_LOG( OPTBIT_remotelog ,) // -R 323 IF_FEATURE_REMOTE_LOG( OPTBIT_remotelog ,) // -R
@@ -330,6 +332,7 @@ enum {
330 OPT_outfile = 1 << OPTBIT_outfile , 332 OPT_outfile = 1 << OPTBIT_outfile ,
331 OPT_loglevel = 1 << OPTBIT_loglevel, 333 OPT_loglevel = 1 << OPTBIT_loglevel,
332 OPT_small = 1 << OPTBIT_small , 334 OPT_small = 1 << OPTBIT_small ,
335 OPT_timestamp = 1 << OPTBIT_timestamp,
333 OPT_filesize = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_filesize )) + 0, 336 OPT_filesize = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_filesize )) + 0,
334 OPT_rotatecnt = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_rotatecnt )) + 0, 337 OPT_rotatecnt = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_rotatecnt )) + 0,
335 OPT_remotelog = IF_FEATURE_REMOTE_LOG( (1 << OPTBIT_remotelog )) + 0, 338 OPT_remotelog = IF_FEATURE_REMOTE_LOG( (1 << OPTBIT_remotelog )) + 0,
@@ -339,7 +342,7 @@ enum {
339 OPT_cfg = IF_FEATURE_SYSLOGD_CFG( (1 << OPTBIT_cfg )) + 0, 342 OPT_cfg = IF_FEATURE_SYSLOGD_CFG( (1 << OPTBIT_cfg )) + 0,
340 OPT_kmsg = IF_FEATURE_KMSG_SYSLOG( (1 << OPTBIT_kmsg )) + 0, 343 OPT_kmsg = IF_FEATURE_KMSG_SYSLOG( (1 << OPTBIT_kmsg )) + 0,
341}; 344};
342#define OPTION_STR "m:nO:l:S" \ 345#define OPTION_STR "m:nO:l:St" \
343 IF_FEATURE_ROTATE_LOGFILE("s:" ) \ 346 IF_FEATURE_ROTATE_LOGFILE("s:" ) \
344 IF_FEATURE_ROTATE_LOGFILE("b:" ) \ 347 IF_FEATURE_ROTATE_LOGFILE("b:" ) \
345 IF_FEATURE_REMOTE_LOG( "R:*") \ 348 IF_FEATURE_REMOTE_LOG( "R:*") \
@@ -813,21 +816,27 @@ static void parse_fac_prio_20(int pri, char *res20)
813 * that there is no timestamp, short-circuiting the test. */ 816 * that there is no timestamp, short-circuiting the test. */
814static void timestamp_and_log(int pri, char *msg, int len) 817static void timestamp_and_log(int pri, char *msg, int len)
815{ 818{
816 char *timestamp; 819 char *timestamp = NULL;
817 time_t now; 820 time_t now;
818 821
819 /* Jan 18 00:11:22 msg... */ 822 /* Jan 18 00:11:22 msg... */
820 /* 01234567890123456 */ 823 /* 01234567890123456 */
821 if (len < 16 || msg[3] != ' ' || msg[6] != ' ' 824 if (len >= 16 && msg[3] == ' ' && msg[6] == ' '
822 || msg[9] != ':' || msg[12] != ':' || msg[15] != ' ' 825 && msg[9] == ':' && msg[12] == ':' && msg[15] == ' '
823 ) { 826 ) {
827 if (!(option_mask32 & OPT_timestamp)) {
828 /* use message timestamp */
829 timestamp = msg;
830 now = 0;
831 }
832 msg += 16;
833 }
834
835 if (!timestamp) {
824 time(&now); 836 time(&now);
825 timestamp = ctime(&now) + 4; /* skip day of week */ 837 timestamp = ctime(&now) + 4; /* skip day of week */
826 } else {
827 now = 0;
828 timestamp = msg;
829 msg += 16;
830 } 838 }
839
831 timestamp[15] = '\0'; 840 timestamp[15] = '\0';
832 841
833 if (option_mask32 & OPT_kmsg) { 842 if (option_mask32 & OPT_kmsg) {
diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c
index cdcba0a03..288b9235f 100644
--- a/util-linux/fdisk.c
+++ b/util-linux/fdisk.c
@@ -169,9 +169,9 @@ typedef unsigned long long ullong;
169 * do not support more than 2^32 sectors 169 * do not support more than 2^32 sectors
170 */ 170 */
171typedef uint32_t sector_t; 171typedef uint32_t sector_t;
172#if UINT_MAX == 4294967295 172#if UINT_MAX == 0xffffffff
173# define SECT_FMT "" 173# define SECT_FMT ""
174#elif ULONG_MAX == 4294967295 174#elif ULONG_MAX == 0xffffffff
175# define SECT_FMT "l" 175# define SECT_FMT "l"
176#else 176#else
177# error Cant detect sizeof(uint32_t) 177# error Cant detect sizeof(uint32_t)
@@ -426,7 +426,7 @@ struct globals {
426 unsigned sector_offset; // = 1; 426 unsigned sector_offset; // = 1;
427 unsigned g_heads, g_sectors, g_cylinders; 427 unsigned g_heads, g_sectors, g_cylinders;
428 smallint /* enum label_type */ current_label_type; 428 smallint /* enum label_type */ current_label_type;
429 smallint display_in_cyl_units; // = 1; 429 smallint display_in_cyl_units;
430#if ENABLE_FEATURE_OSF_LABEL 430#if ENABLE_FEATURE_OSF_LABEL
431 smallint possibly_osf_label; 431 smallint possibly_osf_label;
432#endif 432#endif
@@ -488,7 +488,6 @@ struct globals {
488 sector_size = DEFAULT_SECTOR_SIZE; \ 488 sector_size = DEFAULT_SECTOR_SIZE; \
489 sector_offset = 1; \ 489 sector_offset = 1; \
490 g_partitions = 4; \ 490 g_partitions = 4; \
491 display_in_cyl_units = 1; \
492 units_per_sector = 1; \ 491 units_per_sector = 1; \
493 dos_compatible_flag = 1; \ 492 dos_compatible_flag = 1; \
494} while (0) 493} while (0)
@@ -639,25 +638,6 @@ seek_sector(sector_t secno)
639} 638}
640 639
641#if ENABLE_FEATURE_FDISK_WRITABLE 640#if ENABLE_FEATURE_FDISK_WRITABLE
642/* Read line; return 0 or first printable char */
643static int
644read_line(const char *prompt)
645{
646 int sz;
647
648 sz = read_line_input(NULL, prompt, line_buffer, sizeof(line_buffer));
649 if (sz <= 0)
650 exit(EXIT_SUCCESS); /* Ctrl-D or Ctrl-C */
651
652 if (line_buffer[sz-1] == '\n')
653 line_buffer[--sz] = '\0';
654
655 line_ptr = line_buffer;
656 while (*line_ptr != '\0' && (unsigned char)*line_ptr <= ' ')
657 line_ptr++;
658 return *line_ptr;
659}
660
661static void 641static void
662set_all_unchanged(void) 642set_all_unchanged(void)
663{ 643{
@@ -680,6 +660,25 @@ write_part_table_flag(char *b)
680 b[511] = 0xaa; 660 b[511] = 0xaa;
681} 661}
682 662
663/* Read line; return 0 or first printable non-space char */
664static int
665read_line(const char *prompt)
666{
667 int sz;
668
669 sz = read_line_input(NULL, prompt, line_buffer, sizeof(line_buffer));
670 if (sz <= 0)
671 exit(EXIT_SUCCESS); /* Ctrl-D or Ctrl-C */
672
673 if (line_buffer[sz-1] == '\n')
674 line_buffer[--sz] = '\0';
675
676 line_ptr = line_buffer;
677 while (*line_ptr != '\0' && (unsigned char)*line_ptr <= ' ')
678 line_ptr++;
679 return *line_ptr;
680}
681
683static char 682static char
684read_nonempty(const char *mesg) 683read_nonempty(const char *mesg)
685{ 684{
@@ -1614,53 +1613,74 @@ read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char *
1614 1613
1615 if (*line_ptr == '+' || *line_ptr == '-') { 1614 if (*line_ptr == '+' || *line_ptr == '-') {
1616 int minus = (*line_ptr == '-'); 1615 int minus = (*line_ptr == '-');
1617 int absolute = 0; 1616 unsigned scale_shift;
1618 1617
1619 value = atoi(line_ptr + 1); 1618 if (sizeof(value) <= sizeof(long))
1619 value = strtoul(line_ptr + 1, NULL, 10);
1620 else
1621 value = strtoull(line_ptr + 1, NULL, 10);
1620 1622
1621 /* (1) if 2nd char is digit, use_default = 0. 1623 /* (1) if 2nd char is digit, use_default = 0.
1622 * (2) move line_ptr to first non-digit. */ 1624 * (2) move line_ptr to first non-digit.
1625 */
1623 while (isdigit(*++line_ptr)) 1626 while (isdigit(*++line_ptr))
1624 use_default = 0; 1627 use_default = 0;
1625 1628
1626 switch (*line_ptr) { 1629 scale_shift = 0;
1627 case 'c': 1630 switch (*line_ptr | 0x20) {
1628 case 'C':
1629 if (!display_in_cyl_units)
1630 value *= g_heads * g_sectors;
1631 break;
1632 case 'K':
1633 absolute = 1024;
1634 break;
1635 case 'k': 1631 case 'k':
1636 absolute = 1000; 1632 scale_shift = 10; /* 1024 */
1637 break; 1633 break;
1634/*
1635 * fdisk from util-linux 2.31 seems to round '+NNNk' and '+NNNK' to megabytes,
1636 * (512-byte) sector count of the partition does not equal NNN*2:
1637 *
1638 * Last sector, +sectors or +size{K,M,G,T,P} (1953792-1000215215, default 1000215215): +9727k
1639 * Device Boot Start End Sectors Size Id Type
1640 * /dev/sdaN 1953792 1972223 18432 9M 83 Linux <-- size exactly 9*1024*1024 bytes
1641 *
1642 * Last sector, +sectors or +size{K,M,G,T,P} (1953792-1000215215, default 1000215215): +9728k
1643 * /dev/sdaN 1953792 1974271 20480 10M 83 Linux <-- size exactly 10*1024*1024 bytes
1644 *
1645 * If 'k' means 1000 bytes (not 1024), then 9728k = 9728*1000 = 9500*1024,
1646 * exactly halfway from 9000 to 10000, which explains why it jumps to next mbyte
1647 * at this value.
1648 *
1649 * 'm' does not seem to behave this way: it means 1024*1024 bytes.
1650 *
1651 * Not sure we want to copy this. If user says he wants 1234kbyte partition,
1652 * we do _exactly that_: 1234kbytes = 2468 sectors.
1653 */
1638 case 'm': 1654 case 'm':
1639 case 'M': 1655 scale_shift = 20; /* 1024*1024 */
1640 absolute = 1000000;
1641 break; 1656 break;
1642 case 'g': 1657 case 'g':
1643 case 'G': 1658 scale_shift = 30; /* 1024*1024*1024 */
1644 absolute = 1000000000; 1659 break;
1660 case 't':
1661 scale_shift = 40; /* 1024*1024*1024*1024 */
1645 break; 1662 break;
1646 default: 1663 default:
1647 break; 1664 break;
1648 } 1665 }
1649 if (absolute) { 1666 if (scale_shift) {
1650 ullong bytes; 1667 ullong bytes;
1651 unsigned long unit; 1668 unsigned long unit;
1652 1669
1653 bytes = (ullong) value * absolute; 1670 bytes = (ullong) value << scale_shift;
1654 unit = sector_size * units_per_sector; 1671 unit = sector_size * units_per_sector;
1655 bytes += unit/2; /* round */ 1672 bytes += unit/2; /* round */
1656 bytes /= unit; 1673 bytes /= unit;
1657 value = bytes; 1674 value = (bytes != 0 ? bytes - 1 : 0);
1658 } 1675 }
1659 if (minus) 1676 if (minus)
1660 value = -value; 1677 value = -value;
1661 value += base; 1678 value += base;
1662 } else { 1679 } else {
1663 value = atoi(line_ptr); 1680 if (sizeof(value) <= sizeof(long))
1681 value = strtoul(line_ptr, NULL, 10);
1682 else
1683 value = strtoull(line_ptr, NULL, 10);
1664 while (isdigit(*line_ptr)) { 1684 while (isdigit(*line_ptr)) {
1665 line_ptr++; 1685 line_ptr++;
1666 use_default = 0; 1686 use_default = 0;
@@ -1725,8 +1745,9 @@ get_existing_partition(int warn, unsigned max)
1725} 1745}
1726 1746
1727static int 1747static int
1728get_nonexisting_partition(int warn, unsigned max) 1748get_nonexisting_partition(void)
1729{ 1749{
1750 const int max = 4;
1730 int pno = -1; 1751 int pno = -1;
1731 unsigned i; 1752 unsigned i;
1732 1753
@@ -1748,7 +1769,7 @@ get_nonexisting_partition(int warn, unsigned max)
1748 return -1; 1769 return -1;
1749 1770
1750 not_unique: 1771 not_unique:
1751 return get_partition(warn, max); 1772 return get_partition(/*warn*/ 0, max);
1752} 1773}
1753 1774
1754 1775
@@ -2526,8 +2547,9 @@ add_partition(int n, int sys)
2526 stop = limit; 2547 stop = limit;
2527 } else { 2548 } else {
2528 snprintf(mesg, sizeof(mesg), 2549 snprintf(mesg, sizeof(mesg),
2529 "Last %s or +size or +sizeM or +sizeK", 2550 "Last %s or +size{,K,M,G,T}",
2530 str_units(SINGULAR)); 2551 str_units(SINGULAR)
2552 );
2531 stop = read_int(cround(start), cround(limit), cround(limit), cround(start), mesg); 2553 stop = read_int(cround(start), cround(limit), cround(limit), cround(start), mesg);
2532 if (display_in_cyl_units) { 2554 if (display_in_cyl_units) {
2533 stop = stop * units_per_sector - 1; 2555 stop = stop * units_per_sector - 1;
@@ -2611,15 +2633,16 @@ new_partition(void)
2611 } else { 2633 } else {
2612 char c, line[80]; 2634 char c, line[80];
2613 snprintf(line, sizeof(line), 2635 snprintf(line, sizeof(line),
2614 "Command action\n" 2636 "Partition type\n"
2615 " %s\n" 2637 " p primary partition (1-4)\n"
2616 " p primary partition (1-4)\n", 2638 " %s\n",
2617 (extended_offset ? 2639 (extended_offset ?
2618 "l logical (5 or over)" : "e extended")); 2640 "l logical (5 or over)" : "e extended"));
2619 while (1) { 2641 while (1) {
2620 c = read_nonempty(line); 2642 c = read_nonempty(line);
2621 if ((c | 0x20) == 'p') { 2643 c |= 0x20; /* lowercase */
2622 i = get_nonexisting_partition(0, 4); 2644 if (c == 'p') {
2645 i = get_nonexisting_partition();
2623 if (i >= 0) 2646 if (i >= 0)
2624 add_partition(i, LINUX_NATIVE); 2647 add_partition(i, LINUX_NATIVE);
2625 return; 2648 return;
@@ -2629,7 +2652,7 @@ new_partition(void)
2629 return; 2652 return;
2630 } 2653 }
2631 if (c == 'e' && !extended_offset) { 2654 if (c == 'e' && !extended_offset) {
2632 i = get_nonexisting_partition(0, 4); 2655 i = get_nonexisting_partition();
2633 if (i >= 0) 2656 if (i >= 0)
2634 add_partition(i, EXTENDED); 2657 add_partition(i, EXTENDED);
2635 return; 2658 return;
diff --git a/util-linux/fdisk_sgi.c b/util-linux/fdisk_sgi.c
index 1cf0af5cc..0e5491a19 100644
--- a/util-linux/fdisk_sgi.c
+++ b/util-linux/fdisk_sgi.c
@@ -623,7 +623,7 @@ sgi_change_sysid(int i, int sys)
623 "retrieve from its directory standalone tools like sash and fx.\n" 623 "retrieve from its directory standalone tools like sash and fx.\n"
624 "Only the \"SGI volume\" entire disk section may violate this.\n" 624 "Only the \"SGI volume\" entire disk section may violate this.\n"
625 "Type YES if you are sure about tagging this partition differently.\n"); 625 "Type YES if you are sure about tagging this partition differently.\n");
626 if (strcmp(line_ptr, "YES\n") != 0) 626 if (strcmp(line_ptr, "YES") != 0)
627 return; 627 return;
628 } 628 }
629 sgilabel->partitions[i].id = SGI_SSWAP32(sys); 629 sgilabel->partitions[i].id = SGI_SSWAP32(sys);
diff --git a/util-linux/fdisk_sun.c b/util-linux/fdisk_sun.c
index e32740dea..3697a69b9 100644
--- a/util-linux/fdisk_sun.c
+++ b/util-linux/fdisk_sun.c
@@ -606,7 +606,7 @@ sun_change_sysid(int i, int sys)
606 "there may destroy your partition table and bootblock.\n" 606 "there may destroy your partition table and bootblock.\n"
607 "Type YES if you're very sure you would like that partition\n" 607 "Type YES if you're very sure you would like that partition\n"
608 "tagged with 82 (Linux swap): "); 608 "tagged with 82 (Linux swap): ");
609 if (strcmp (line_ptr, "YES\n")) 609 if (strcmp(line_ptr, "YES") != 0)
610 return; 610 return;
611 } 611 }
612 switch (sys) { 612 switch (sys) {