diff options
author | Ron Yorston <rmy@pobox.com> | 2018-09-10 14:37:07 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2018-09-10 14:59:33 +0100 |
commit | d89ced75b204f0eb5611f522864beb81d1b393f5 (patch) | |
tree | 5daa31427e287fe079a0ef551097753773fdb266 | |
parent | f72845d9332fa6311a46dbcad3180d5008182982 (diff) | |
parent | 05b18065ab9c375f6185b65a3631d4c6cc1a4be9 (diff) | |
download | busybox-w32-d89ced75b204f0eb5611f522864beb81d1b393f5.tar.gz busybox-w32-d89ced75b204f0eb5611f522864beb81d1b393f5.tar.bz2 busybox-w32-d89ced75b204f0eb5611f522864beb81d1b393f5.zip |
Merge branch 'busybox' into merge
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 | |||
83 | for i in $h; do | 83 | for 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 */ |
602 | static void NOINLINE vfork_compressor(int tar_fd, const char *gzip) | 602 | static 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 |
764 | static llist_t *append_file_list_to_list(llist_t *list) | 769 | static 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 | ||
70 | static 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 | |||
79 | int sleep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 62 | int sleep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
80 | int sleep_main(int argc UNUSED_PARAM, char **argv) | 63 | int 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 | ||
11 | echo "$1" >rundir/sync_status | ||
12 | |||
11 | dt=`date '+%Y-%m-%d %H:%M:%S'` | 13 | dt=`date '+%Y-%m-%d %H:%M:%S'` |
12 | 14 | ||
13 | echo "`tail -n 199 -- "$0.log" 2>/dev/null`" >"$0.log.$$" | 15 | echo "`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 */ |
1063 | uint16_t xatou16(const char *numstr) FAST_FUNC; | 1063 | uint16_t xatou16(const char *numstr) FAST_FUNC; |
1064 | 1064 | ||
1065 | #if ENABLE_FLOAT_DURATION | ||
1066 | typedef double duration_t; | ||
1067 | void sleep_for_duration(duration_t duration) FAST_FUNC; | ||
1068 | #else | ||
1069 | typedef unsigned duration_t; | ||
1070 | #define sleep_for_duration(duration) sleep(duration) | ||
1071 | #endif | ||
1072 | duration_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 | |||
23 | static 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 | ||
32 | duration_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 | } | ||
58 | void 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 | ||
74 | duration_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 */ |
48 | char* FAST_FUNC xmalloc_fgets(FILE *file) | 48 | char* 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 */ |
53 | char* FAST_FUNC xmalloc_fgetline(FILE *file) | 55 | char* 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 | ||
110 | void FAST_FUNC encode_base64(char *fname, const char *text, const char *eol) | 110 | static 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 | ||
154 | void FAST_FUNC printstr_base64(const char *text) | ||
155 | { | ||
156 | encode_n_base64(NULL, text, strlen(text)); | ||
157 | } | ||
158 | |||
159 | void FAST_FUNC printbuf_base64(const char *text, unsigned len) | ||
160 | { | ||
161 | encode_n_base64(NULL, text, len); | ||
162 | } | ||
163 | |||
164 | void 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 | ||
35 | char *send_mail_command(const char *fmt, const char *param) FAST_FUNC; | 35 | char *send_mail_command(const char *fmt, const char *param) FAST_FUNC; |
36 | 36 | ||
37 | void encode_base64(char *fname, const char *text, const char *eol) FAST_FUNC; | 37 | void printbuf_base64(const char *buf, unsigned len) FAST_FUNC; |
38 | void printstr_base64(const char *buf) FAST_FUNC; | ||
39 | void 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 | ||
690 | static void add_query(int type, const char *dname) | 728 | static 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 | ||
750 | static 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 | |||
712 | static char *make_ptr(const char *addrstr) | 772 | static 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") |
354 | enum { | 356 | enum { |
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) | |||
173 | static const char *valid_domain_label(const char *label) | 173 | static 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 |
904 | static unsigned handle_input(unsigned scan_mask, unsigned interval) | 904 | static 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) | |||
1092 | int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 1092 | int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1093 | int top_main(int argc UNUSED_PARAM, char **argv) | 1093 | int 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 | |||
57 | int uptime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 55 | int uptime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
58 | int uptime_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | 56 | int 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(¤t_secs); | 66 | time(¤t_secs); |
67 | sysinfo(&info); | ||
68 | |||
69 | if (opts) // -s | ||
70 | current_secs -= info.uptime; | ||
71 | |||
66 | current_time = localtime(¤t_secs); | 72 | current_time = localtime(¤t_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 | } |
138 | skip: | 140 | skip: |
@@ -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 | */ |
7551 | static NOINLINE ssize_t | 7557 | static NOINLINE ssize_t |
7552 | varvalue(char *name, int varflags, int flags, int *quotedp) | 7558 | varvalue(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, "ed); | 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 | */ |
10434 | static void unwindfiles(struct parsefile *stop); | ||
10416 | static int | 10435 | static int |
10417 | isassignment(const char *p) | 10436 | isassignment(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 | ||
11302 | static void | ||
11303 | unwindfiles(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 | */ |
11284 | static void | 11312 | static void |
11285 | popallfiles(void) | 11313 | popallfiles(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) | |||
14297 | static int FAST_FUNC | 14325 | static int FAST_FUNC |
14298 | readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | 14326 | readcmd(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(¶ms, 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(¶ms); |
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 @@ | |||
1 | Unquoted non-matching glob in var:'test*.TMP/\name_doesnt_exist' | ||
2 | Unquoted matching glob in var: 'testdir.TMP/name' | ||
3 | Quoted non-matching glob in var: 'test*.TMP/\name_doesnt_exist' | ||
4 | Quoted 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 @@ | |||
1 | mkdir testdir.TMP | ||
2 | >testdir.TMP/name | ||
3 | a="test*.TMP/\name_doesnt_exist" | ||
4 | b="test*.TMP/\name" | ||
5 | printf "Unquoted non-matching glob in var:'%s'\n" $a | ||
6 | printf "Unquoted matching glob in var: '%s'\n" $b | ||
7 | printf "Quoted non-matching glob in var: '%s'\n" "$a" | ||
8 | printf "Quoted matching glob in var: '%s'\n" "$b" | ||
9 | rm -f testdir.TMP/name | ||
10 | rmdir 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 @@ | |||
1 | false | ||
2 | for v; do | ||
3 | exit 2 | ||
4 | done | ||
5 | echo 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 @@ | |||
1 | var=val | 1 | var=val |
2 | var=val | 2 | var=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. |
7 | var=val f | 7 | var=val f |
8 | echo "var=$var" | 8 | echo "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 @@ | |||
1 | v=/dev/ram | ||
2 | echo ${v////-} | ||
3 | echo ${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 @@ | |||
1 | should be empty: '' | 1 | should be empty: '' |
2 | should be empty: '' | 2 | should be empty: '' |
3 | should be not empty: 'val2' | 3 | should be not empty: 'val2' |
4 | should be not empty: 'val3' | 4 | should 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='' | |||
15 | VAR=val2 exec 2>&1 | 15 | VAR=val2 exec 2>&1 |
16 | echo "should be not empty: '$VAR'" | 16 | echo "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) | ||
20 | f() { true; } | 18 | f() { true; } |
21 | VAR='' | 19 | VAR='' |
22 | VAR=val3 f | 20 | VAR=val3 f |
23 | echo "should be not empty: '$VAR'" | 21 | echo "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 @@ | |||
1 | IFS= | ||
2 | set -- | ||
3 | set -- $@ $* | ||
4 | echo 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 | ||
2400 | static 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 */ |
2380 | static void set_pwd_var(unsigned flag) | 2407 | static 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) | ||
2427 | static 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 | ||
2921 | static 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 |
2904 | static void nommu_addchr(o_string *o, int ch) | 2927 | static 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 | ||
2913 | static void o_addstr_with_NUL(o_string *o, const char *str) | 2936 | #if ENABLE_HUSH_MODE_X |
2937 | static 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 | } | ||
2941 | static void x_mode_addstr(const char *str) | ||
2942 | { | ||
2943 | o_addstr(&G.x_mode_buf, str); | ||
2916 | } | 2944 | } |
2945 | static void x_mode_addblock(const char *str, int len) | ||
2946 | { | ||
2947 | o_addblock(&G.x_mode_buf, str, len); | ||
2948 | } | ||
2949 | static void x_mode_prefix(void) | ||
2950 | { | ||
2951 | int n = G.x_mode_depth; | ||
2952 | do x_mode_addchr('+'); while (--n >= 0); | ||
2953 | } | ||
2954 | static 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 | ||
5963 | static 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 | */ |
5970 | static int encode_then_append_var_plusminus(o_string *output, int n, | 6085 | static 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 |
8127 | static 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 | } | ||
8017 | static void dump_cmd_in_x_mode(char **argv) | 8167 | static 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) | |||
10374 | static int FAST_FUNC builtin_read(char **argv) | 10535 | static 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(¶ms, 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:", ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.opt_u, ¶ms.opt_d |
10391 | #else | 10548 | #else |
10392 | "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u | 10549 | "!srn:p:t:u:", ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.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(¶ms); |
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 @@ | |||
1 | Unquoted non-matching glob in var:'test*.TMP/\name_doesnt_exist' | ||
2 | Unquoted matching glob in var: 'testdir.TMP/name' | ||
3 | Quoted non-matching glob in var: 'test*.TMP/\name_doesnt_exist' | ||
4 | Quoted 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 @@ | |||
1 | mkdir testdir.TMP | ||
2 | >testdir.TMP/name | ||
3 | a="test*.TMP/\name_doesnt_exist" | ||
4 | b="test*.TMP/\name" | ||
5 | printf "Unquoted non-matching glob in var:'%s'\n" $a | ||
6 | printf "Unquoted matching glob in var: '%s'\n" $b | ||
7 | printf "Quoted non-matching glob in var: '%s'\n" "$a" | ||
8 | printf "Quoted matching glob in var: '%s'\n" "$b" | ||
9 | rm -f testdir.TMP/name | ||
10 | rmdir 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 @@ | |||
1 | false | ||
2 | for v; do | ||
3 | exit 2 | ||
4 | done | ||
5 | echo 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. |
7 | var=val f | 7 | var=val f |
8 | echo "var=$var" | 8 | echo "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 | '' | ||
2 | Ok: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 @@ | |||
1 | x="''''"; echo "${x#"${x+''}"''}" | ||
2 | echo 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 @@ | |||
1 | v=/dev/ram | ||
2 | echo ${v////-} | ||
3 | echo ${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 @@ | |||
1 | IFS= | ||
2 | set -- | ||
3 | set -- $@ $* | ||
4 | echo 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. |
52 | const char* FAST_FUNC | 52 | const char* FAST_FUNC |
53 | shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | 53 | shell_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 | ||
33 | struct 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 | }; | ||
33 | enum { | 44 | enum { |
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 |
42 | const char* FAST_FUNC | 53 | const char* FAST_FUNC |
43 | shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | 54 | shell_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 | ||
54 | int FAST_FUNC | 56 | int FAST_FUNC |
55 | shell_builtin_ulimit(char **argv); | 57 | shell_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 | ||
86 | static int klogd_read(char *bufp, int len) | 86 | static 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. */ |
814 | static void timestamp_and_log(int pri, char *msg, int len) | 817 | static 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 | */ |
171 | typedef uint32_t sector_t; | 171 | typedef 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 */ | ||
643 | static int | ||
644 | read_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 | |||
661 | static void | 641 | static void |
662 | set_all_unchanged(void) | 642 | set_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 */ | ||
664 | static int | ||
665 | read_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 | |||
683 | static char | 682 | static char |
684 | read_nonempty(const char *mesg) | 683 | read_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 | ||
1727 | static int | 1747 | static int |
1728 | get_nonexisting_partition(int warn, unsigned max) | 1748 | get_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) { |