aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2017-07-29 09:55:08 +0100
committerRon Yorston <rmy@pobox.com>2017-07-29 09:55:08 +0100
commit86d60bb0ceb277e500a8daabd995bc713bbdadc9 (patch)
tree3e439f92d5a3fec2546d526579cc85e98f066e40
parentb30c60a9786a1608211a96755996bd6c02951a27 (diff)
parent69be994de69d794f038f10a3e7a67519b2006581 (diff)
downloadbusybox-w32-86d60bb0ceb277e500a8daabd995bc713bbdadc9.tar.gz
busybox-w32-86d60bb0ceb277e500a8daabd995bc713bbdadc9.tar.bz2
busybox-w32-86d60bb0ceb277e500a8daabd995bc713bbdadc9.zip
Merge branch 'busybox' into merge
-rw-r--r--Config.in96
-rw-r--r--archival/libarchive/data_extract_all.c42
-rw-r--r--archival/tar.c37
-rwxr-xr-xarchival/tar_symlink_attack16
-rw-r--r--archival/unzip.c1
-rw-r--r--coreutils/link.c2
-rw-r--r--editors/awk.c3
-rw-r--r--editors/ed.c1118
-rw-r--r--examples/var_service/README14
-rw-r--r--examples/var_service/README_distro_proposal.txt2
-rw-r--r--include/bb_archive.h4
-rw-r--r--include/platform.h11
-rw-r--r--libbb/Config.src5
-rw-r--r--libbb/appletlib.c41
-rw-r--r--libbb/lineedit.c4
-rw-r--r--libbb/u_signal_names.c2
-rw-r--r--loginutils/login.c2
-rw-r--r--loginutils/passwd.c2
-rw-r--r--loginutils/vlock.c2
-rw-r--r--miscutils/crond.c2
-rw-r--r--miscutils/crontab.c2
-rw-r--r--networking/ftpd.c13
-rw-r--r--networking/httpd.c4
-rw-r--r--networking/ifupdown.c4
-rw-r--r--networking/inetd.c39
-rw-r--r--networking/ip.c18
-rw-r--r--networking/telnetd.c2
-rw-r--r--networking/tftp.c16
-rw-r--r--networking/udhcp/Config.src80
-rw-r--r--networking/udhcp/d6_dhcpc.c2
-rw-r--r--networking/udhcp/socket.c2
-rw-r--r--procps/kill.c2
-rw-r--r--procps/ps.c11
-rw-r--r--shell/ash.c459
-rw-r--r--shell/ash_test/ash-misc/assignment2.right2
-rwxr-xr-xshell/ash_test/ash-misc/assignment2.tests3
-rw-r--r--shell/ash_test/ash-misc/empty_args.right6
-rwxr-xr-xshell/ash_test/ash-misc/empty_args.tests9
-rw-r--r--shell/ash_test/ash-misc/env_and_func.right2
-rwxr-xr-xshell/ash_test/ash-misc/env_and_func.tests8
-rw-r--r--shell/ash_test/ash-psubst/emptytick.right17
-rwxr-xr-xshell/ash_test/ash-psubst/emptytick.tests16
-rw-r--r--shell/ash_test/ash-psubst/tick.right2
-rwxr-xr-xshell/ash_test/ash-psubst/tick.tests4
-rw-r--r--shell/ash_test/ash-psubst/tick2.right1
-rwxr-xr-xshell/ash_test/ash-psubst/tick2.tests5
-rw-r--r--shell/ash_test/ash-psubst/tick3.right6
-rwxr-xr-xshell/ash_test/ash-psubst/tick3.tests14
-rw-r--r--shell/ash_test/ash-psubst/tick4.right7
-rwxr-xr-xshell/ash_test/ash-psubst/tick4.tests7
-rw-r--r--shell/ash_test/ash-psubst/tick_huge.right3
-rwxr-xr-xshell/ash_test/ash-psubst/tick_huge.tests7
-rw-r--r--shell/ash_test/ash-signals/catch.right5
-rwxr-xr-xshell/ash_test/ash-signals/catch.tests20
-rw-r--r--shell/ash_test/ash-signals/signal_read2.right2
-rwxr-xr-xshell/ash_test/ash-signals/signal_read2.tests7
-rw-r--r--shell/ash_test/ash-signals/subshell.right21
-rwxr-xr-xshell/ash_test/ash-signals/subshell.tests19
-rw-r--r--shell/ash_test/ash-vars/param_expand_alt.right9
-rwxr-xr-xshell/ash_test/ash-vars/param_expand_alt.tests33
-rw-r--r--shell/ash_test/ash-vars/param_expand_assign.right27
-rwxr-xr-xshell/ash_test/ash-vars/param_expand_assign.tests39
-rw-r--r--shell/ash_test/ash-vars/param_expand_bash_substring.right64
-rwxr-xr-xshell/ash_test/ash-vars/param_expand_bash_substring.tests84
-rw-r--r--shell/ash_test/ash-vars/param_expand_default.right7
-rwxr-xr-xshell/ash_test/ash-vars/param_expand_default.tests23
-rw-r--r--shell/ash_test/ash-vars/param_expand_indicate_error.right43
-rwxr-xr-xshell/ash_test/ash-vars/param_expand_indicate_error.tests61
-rw-r--r--shell/ash_test/ash-vars/param_expand_len1.right11
-rwxr-xr-xshell/ash_test/ash-vars/param_expand_len1.tests31
-rw-r--r--shell/ash_test/ash-vars/readonly0.right2
-rw-r--r--shell/ash_test/ash-vars/unset.right17
-rwxr-xr-xshell/ash_test/ash-vars/unset.tests40
-rw-r--r--shell/hush.c67
-rwxr-xr-xshell/hush_test/hush-misc/env_and_func.tests4
-rw-r--r--shell/hush_test/hush-redir/redir.right2
-rwxr-xr-xshell/hush_test/hush-redir/redir.tests6
-rw-r--r--shell/hush_test/hush-vars/param_expand_alt.right3
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_alt.tests21
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_assign.tests27
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_bash_substring.tests13
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_default.tests6
-rw-r--r--shell/hush_test/hush-vars/param_expand_indicate_error.right2
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_indicate_error.tests2
-rw-r--r--shell/hush_test/hush-vars/param_expand_len1.right11
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_len1.tests31
-rw-r--r--shell/hush_test/hush-vars/unset.right4
-rwxr-xr-xshell/hush_test/hush-vars/unset.tests7
-rwxr-xr-xtestsuite/tar.tests65
-rw-r--r--util-linux/blkid.c2
-rw-r--r--util-linux/findfs.c2
-rw-r--r--util-linux/getopt.c2
-rw-r--r--util-linux/mdev.c2
-rw-r--r--util-linux/mount.c10
-rw-r--r--util-linux/rdate.c9
-rw-r--r--util-linux/swaponoff.c11
-rw-r--r--util-linux/umount.c2
-rw-r--r--util-linux/volume_id/get_devname.c1
-rw-r--r--util-linux/volume_id/minix.c (renamed from util-linux/volume_id/unused_minix.c)59
-rw-r--r--util-linux/volume_id/volume_id.c1
-rw-r--r--util-linux/volume_id/volume_id_internal.h2
101 files changed, 2017 insertions, 1101 deletions
diff --git a/Config.in b/Config.in
index 2342d8467..e131dafaf 100644
--- a/Config.in
+++ b/Config.in
@@ -3,7 +3,7 @@
3# see scripts/kbuild/config-language.txt. 3# see scripts/kbuild/config-language.txt.
4# 4#
5 5
6mainmenu "BusyBox Configuration" 6mainmenu "Configuration"
7 7
8config HAVE_DOT_CONFIG 8config HAVE_DOT_CONFIG
9 bool 9 bool
@@ -23,7 +23,7 @@ config PLATFORM_MINGW32
23 23
24endchoice 24endchoice
25 25
26menu "Busybox Settings" 26menu "Settings"
27 27
28config DESKTOP 28config DESKTOP
29 bool "Enable compatibility for full-blown desktop systems" 29 bool "Enable compatibility for full-blown desktop systems"
@@ -71,7 +71,7 @@ config SHOW_USAGE
71 bool "Show applet usage messages" 71 bool "Show applet usage messages"
72 default y 72 default y
73 help 73 help
74 Enabling this option, BusyBox applets will show terse help messages 74 Enabling this option, applets will show terse help messages
75 when invoked with wrong arguments. 75 when invoked with wrong arguments.
76 If you do not want to show any (helpful) usage message when 76 If you do not want to show any (helpful) usage message when
77 issuing wrong command syntax, you can say 'N' here, 77 issuing wrong command syntax, you can say 'N' here,
@@ -82,18 +82,16 @@ config FEATURE_VERBOSE_USAGE
82 default y 82 default y
83 depends on SHOW_USAGE 83 depends on SHOW_USAGE
84 help 84 help
85 All BusyBox applets will show verbose help messages when 85 All applets will show verbose help messages when invoked with --help.
86 busybox is invoked with --help. This will add a lot of text to the 86 This will add a lot of text to the binary.
87 busybox binary. In the default configuration, this will add about
88 13k, but it can add much more depending on your configuration.
89 87
90config FEATURE_COMPRESS_USAGE 88config FEATURE_COMPRESS_USAGE
91 bool "Store applet usage messages in compressed form" 89 bool "Store applet usage messages in compressed form"
92 default y 90 default y
93 depends on SHOW_USAGE 91 depends on SHOW_USAGE
94 help 92 help
95 Store usage messages in .bz compressed form, uncompress them 93 Store usage messages in .bz2 compressed form, uncompress them
96 on-the-fly when <applet> --help is called. 94 on-the-fly when "APPLET --help" is run.
97 95
98 If you have a really tiny busybox with few applets enabled (and 96 If you have a really tiny busybox with few applets enabled (and
99 bunzip2 isn't one of them), the overhead of the decompressor might 97 bunzip2 isn't one of them), the overhead of the decompressor might
@@ -105,12 +103,11 @@ config LFS
105 bool "Support files > 2 GB" 103 bool "Support files > 2 GB"
106 default y 104 default y
107 help 105 help
108 If you want to build BusyBox with large file support, then enable 106 If you need to work with large files, enable this option.
109 this option. This will have no effect if your kernel or your C 107 This will have no effect if your kernel or your C
110 library lacks large file support for large files. Some of the 108 library lacks large file support for large files. Some of the
111 programs that can benefit from large file support include dd, gzip, 109 programs that can benefit from large file support include dd, gzip,
112 cp, mount, tar, and many others. If you want to access files larger 110 cp, mount, tar.
113 than 2 Gigabytes, enable this option.
114 111
115config GLOBBING 112config GLOBBING
116 bool "Allow busybox.exe to expand wildcards" 113 bool "Allow busybox.exe to expand wildcards"
@@ -138,14 +135,14 @@ config PAM
138 bool "Support PAM (Pluggable Authentication Modules)" 135 bool "Support PAM (Pluggable Authentication Modules)"
139 default n 136 default n
140 help 137 help
141 Use PAM in some busybox applets (currently login and httpd) instead 138 Use PAM in some applets (currently login and httpd) instead
142 of direct access to password database. 139 of direct access to password database.
143 140
144config FEATURE_DEVPTS 141config FEATURE_DEVPTS
145 bool "Use the devpts filesystem for Unix98 PTYs" 142 bool "Use the devpts filesystem for Unix98 PTYs"
146 default y 143 default y
147 help 144 help
148 Enable if you want BusyBox to use Unix98 PTY support. If enabled, 145 Enable if you want to use Unix98 PTY support. If enabled,
149 busybox will use /dev/ptmx for the master side of the pseudoterminal 146 busybox will use /dev/ptmx for the master side of the pseudoterminal
150 and /dev/pts/<number> for the slave side. Otherwise, BSD style 147 and /dev/pts/<number> for the slave side. Otherwise, BSD style
151 /dev/ttyp<number> will be used. To use this option, you should have 148 /dev/ttyp<number> will be used. To use this option, you should have
@@ -193,9 +190,9 @@ config BUSYBOX
193 bool "Include busybox applet" 190 bool "Include busybox applet"
194 default y 191 default y
195 help 192 help
196 The busybox applet provides general help regarding busybox and 193 The busybox applet provides general help message and allows
197 allows the included applets to be listed. It's also required 194 the included applets to be listed. It also provides
198 if applet links are to be installed at runtime. If you unselect 195 optional --install command to create applet links. If you unselect
199 this option, running busybox without any arguments will give 196 this option, running busybox without any arguments will give
200 just a cryptic error message: 197 just a cryptic error message:
201 198
@@ -217,7 +214,7 @@ config INSTALL_NO_USR
217 bool "Don't use /usr" 214 bool "Don't use /usr"
218 default n 215 default n
219 help 216 help
220 Disable use of /usr. busybox --install and "make install" 217 Disable use of /usr. "busybox --install" and "make install"
221 will install applets only to /bin and /sbin, 218 will install applets only to /bin and /sbin,
222 never to /usr/bin or /usr/sbin. 219 never to /usr/bin or /usr/sbin.
223 220
@@ -230,11 +227,11 @@ config FEATURE_SUID
230 root-level operations even when run by ordinary users 227 root-level operations even when run by ordinary users
231 (for example, mounting of user mounts in fstab needs this). 228 (for example, mounting of user mounts in fstab needs this).
232 229
233 With this option enabled, Busybox drops privileges for applets 230 With this option enabled, busybox drops privileges for applets
234 that don't need root access, before entering their main() function. 231 that don't need root access, before entering their main() function.
235 232
236 If you are really paranoid and don't want even initial busybox code 233 If you are really paranoid and don't want even initial busybox code
237 to run under root for evey applet, build two busybox binaries with 234 to run under root for every applet, build two busybox binaries with
238 different applets in them (and the appropriate symlinks pointing 235 different applets in them (and the appropriate symlinks pointing
239 to each binary), and only set the suid bit on the one that needs it. 236 to each binary), and only set the suid bit on the one that needs it.
240 237
@@ -320,14 +317,14 @@ config FEATURE_PREFER_APPLETS
320 (command name can be shown as 'exe' for applets started this way). 317 (command name can be shown as 'exe' for applets started this way).
321 318
322config BUSYBOX_EXEC_PATH 319config BUSYBOX_EXEC_PATH
323 string "Path to BusyBox executable" 320 string "Path to busybox executable"
324 default "/proc/self/exe" 321 default "/proc/self/exe"
325 help 322 help
326 When Busybox applets need to run other busybox applets, BusyBox 323 When applets need to run other applets, busybox
327 sometimes needs to exec() itself. When the /proc filesystem is 324 sometimes needs to exec() itself. When the /proc filesystem is
328 mounted, /proc/self/exe always points to the currently running 325 mounted, /proc/self/exe always points to the currently running
329 executable. If you haven't got /proc, set this to wherever you 326 executable. If you haven't got /proc, set this to wherever you
330 want to run BusyBox from. 327 want to run busybox from.
331 328
332config SELINUX 329config SELINUX
333 bool "Support NSA Security Enhanced Linux" 330 bool "Support NSA Security Enhanced Linux"
@@ -390,21 +387,17 @@ config PLATFORM_LINUX
390comment 'Build Options' 387comment 'Build Options'
391 388
392config STATIC 389config STATIC
393 bool "Build BusyBox as a static binary (no shared libs)" 390 bool "Build static binary (no shared libs)"
394 default n 391 default n
395 help 392 help
396 If you want to build a static BusyBox binary, which does not 393 If you want to build a static binary, which does not use
397 use or require any shared libraries, then enable this option. 394 or require any shared libraries, enable this option.
398 This can cause BusyBox to be considerably larger, so you should 395 Static binaries are larger, but do not require functioning
399 leave this option false unless you have a good reason (i.e. 396 dynamic libraries to be present, which is important if used
400 your target platform does not support shared libraries, or 397 as a system rescue tool.
401 you are building an initrd which doesn't need anything but
402 BusyBox, etc).
403
404 Most people will leave this set to 'N'.
405 398
406config PIE 399config PIE
407 bool "Build BusyBox as a position independent executable" 400 bool "Build position independent executable"
408 default n 401 default n
409 depends on !STATIC 402 depends on !STATIC
410 help 403 help
@@ -502,10 +495,10 @@ config FEATURE_SHARED_BUSYBOX
502### Say 'N' unless you know what you are doing. 495### Say 'N' unless you know what you are doing.
503 496
504config CROSS_COMPILER_PREFIX 497config CROSS_COMPILER_PREFIX
505 string "Cross Compiler prefix" 498 string "Cross compiler prefix"
506 default "" 499 default ""
507 help 500 help
508 If you want to build BusyBox with a cross compiler, then you 501 If you want to build busybox with a cross compiler, then you
509 will need to set this to the cross-compiler prefix, for example, 502 will need to set this to the cross-compiler prefix, for example,
510 "i386-uclibc-". 503 "i386-uclibc-".
511 504
@@ -518,11 +511,11 @@ config SYSROOT
518 string "Path to sysroot" 511 string "Path to sysroot"
519 default "" 512 default ""
520 help 513 help
521 If you want to build BusyBox with a cross compiler, then you 514 If you want to build busybox with a cross compiler, then you
522 might also need to specify where /usr/include and /usr/lib 515 might also need to specify where /usr/include and /usr/lib
523 will be found. 516 will be found.
524 517
525 For example, BusyBox can be built against an installed 518 For example, busybox can be built against an installed
526 Android NDK, platform version 9, for ARM ABI with 519 Android NDK, platform version 9, for ARM ABI with
527 520
528 CONFIG_SYSROOT=/opt/android-ndk/platforms/android-9/arch-arm 521 CONFIG_SYSROOT=/opt/android-ndk/platforms/android-9/arch-arm
@@ -616,21 +609,22 @@ config INSTALL_SH_APPLET_SCRIPT_WRAPPER
616endchoice 609endchoice
617 610
618config PREFIX 611config PREFIX
619 string "BusyBox installation prefix" 612 string "Destination path for 'make install'"
620 default "./_install" 613 default "./_install"
621 help 614 help
622 Define your directory to install BusyBox files/subdirs in. 615 Where "make install" should install busybox binary and links.
623 616
624comment 'Debugging Options' 617comment 'Debugging Options'
625 618
626config DEBUG 619config DEBUG
627 bool "Build BusyBox with extra Debugging symbols" 620 bool "Build with debug information"
628 default n 621 default n
629 help 622 help
630 Say Y here if you wish to examine BusyBox internals while applets are 623 Say Y here to compile with debug information.
631 running. This increases the size of the binary considerably, and 624 This increases the size of the binary considerably, and
632 should only be used when doing development. If you are doing 625 should only be used when doing development.
633 development and want to debug BusyBox, answer Y. 626
627 This adds -g option to gcc command line.
634 628
635 Most people should answer N. 629 Most people should answer N.
636 630
@@ -645,6 +639,8 @@ config DEBUG_PESSIMIZE
645 in a much bigger executable that more closely matches the source 639 in a much bigger executable that more closely matches the source
646 code. 640 code.
647 641
642 This replaces -Os/-O2 with -O0 in gcc command line.
643
648config DEBUG_SANITIZE 644config DEBUG_SANITIZE
649 bool "Enable runtime sanitizers (ASAN/LSAN/USAN/etc...)" 645 bool "Enable runtime sanitizers (ASAN/LSAN/USAN/etc...)"
650 default n 646 default n
@@ -662,7 +658,7 @@ config UNIT_TEST
662 default n 658 default n
663 help 659 help
664 Say Y here if you want to build unit tests (both the framework and 660 Say Y here if you want to build unit tests (both the framework and
665 test cases) as a Busybox applet. This results in bigger code, so you 661 test cases) as an applet. This results in bigger code, so you
666 probably don't want this option in production builds. 662 probably don't want this option in production builds.
667 663
668config WERROR 664config WERROR
@@ -677,8 +673,8 @@ choice
677 prompt "Additional debugging library" 673 prompt "Additional debugging library"
678 default NO_DEBUG_LIB 674 default NO_DEBUG_LIB
679 help 675 help
680 Using an additional debugging library will make BusyBox become 676 Using an additional debugging library will make busybox become
681 considerable larger and will cause it to run more slowly. You 677 considerably larger and will cause it to run more slowly. You
682 should always leave this option disabled for production use. 678 should always leave this option disabled for production use.
683 679
684 dmalloc support: 680 dmalloc support:
@@ -699,7 +695,7 @@ choice
699 This enables compiling with Electric-fence support. Electric 695 This enables compiling with Electric-fence support. Electric
700 fence is another very useful malloc debugging library which uses 696 fence is another very useful malloc debugging library which uses
701 your computer's virtual memory hardware to detect illegal memory 697 your computer's virtual memory hardware to detect illegal memory
702 accesses. This support will make BusyBox be considerable larger 698 accesses. This support will make busybox be considerably larger
703 and run slower, so you should leave this option disabled unless 699 and run slower, so you should leave this option disabled unless
704 you are hunting a hard to find memory problem. 700 you are hunting a hard to find memory problem.
705 701
diff --git a/archival/libarchive/data_extract_all.c b/archival/libarchive/data_extract_all.c
index 1830ffb8d..1ce927c2f 100644
--- a/archival/libarchive/data_extract_all.c
+++ b/archival/libarchive/data_extract_all.c
@@ -128,10 +128,11 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
128 res = link(hard_link, dst_name); 128 res = link(hard_link, dst_name);
129 if (res != 0 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) { 129 if (res != 0 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) {
130 /* shared message */ 130 /* shared message */
131 bb_perror_msg("can't create %slink " 131 bb_perror_msg("can't create %slink '%s' to '%s'",
132 "%s to %s", "hard", 132 "hard",
133 dst_name, 133 dst_name,
134 hard_link); 134 hard_link
135 );
135 } 136 }
136 /* Hardlinks have no separate mode/ownership, skip chown/chmod */ 137 /* Hardlinks have no separate mode/ownership, skip chown/chmod */
137 goto ret; 138 goto ret;
@@ -178,15 +179,44 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
178 case S_IFLNK: 179 case S_IFLNK:
179 /* Symlink */ 180 /* Symlink */
180//TODO: what if file_header->link_target == NULL (say, corrupted tarball?) 181//TODO: what if file_header->link_target == NULL (say, corrupted tarball?)
182
183 /* To avoid a directory traversal attack via symlinks,
184 * for certain link targets postpone creation of symlinks.
185 *
186 * For example, consider a .tar created via:
187 * $ tar cvf bug.tar anything.txt
188 * $ ln -s /tmp symlink
189 * $ tar --append -f bug.tar symlink
190 * $ rm symlink
191 * $ mkdir symlink
192 * $ tar --append -f bug.tar symlink/evil.py
193 *
194 * This will result in an archive that contains:
195 * $ tar --list -f bug.tar
196 * anything.txt
197 * symlink [-> /tmp]
198 * symlink/evil.py
199 *
200 * Untarring bug.tar would otherwise place evil.py in '/tmp'.
201 */
202 if (file_header->link_target[0] == '/'
203 || strstr(file_header->link_target, "..")
204 ) {
205 llist_add_to(&archive_handle->symlink_placeholders,
206 xasprintf("%s%c%s", file_header->name, '\0', file_header->link_target)
207 );
208 break;
209 }
181 res = symlink(file_header->link_target, dst_name); 210 res = symlink(file_header->link_target, dst_name);
182 if (res != 0 211 if (res != 0
183 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) 212 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
184 ) { 213 ) {
185 /* shared message */ 214 /* shared message */
186 bb_perror_msg("can't create %slink " 215 bb_perror_msg("can't create %slink '%s' to '%s'",
187 "%s to %s", "sym", 216 "sym",
188 dst_name, 217 dst_name,
189 file_header->link_target); 218 file_header->link_target
219 );
190 } 220 }
191 break; 221 break;
192 case S_IFSOCK: 222 case S_IFSOCK:
diff --git a/archival/tar.c b/archival/tar.c
index 4d1db4934..d90a5dc4f 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -22,24 +22,6 @@
22 * 22 *
23 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 23 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
24 */ 24 */
25/* TODO: security with -C DESTDIR option can be enhanced.
26 * Consider tar file created via:
27 * $ tar cvf bug.tar anything.txt
28 * $ ln -s /tmp symlink
29 * $ tar --append -f bug.tar symlink
30 * $ rm symlink
31 * $ mkdir symlink
32 * $ tar --append -f bug.tar symlink/evil.py
33 *
34 * This will result in an archive which contains:
35 * $ tar --list -f bug.tar
36 * anything.txt
37 * symlink
38 * symlink/evil.py
39 *
40 * Untarring it puts evil.py in '/tmp' even if the -C DESTDIR is given.
41 * This doesn't feel right, and IIRC GNU tar doesn't do that.
42 */
43 25
44//config:config TAR 26//config:config TAR
45//config: bool "tar (40 kb)" 27//config: bool "tar (40 kb)"
@@ -296,6 +278,23 @@ static void chksum_and_xwrite(int fd, struct tar_header_t* hp)
296 xwrite(fd, hp, sizeof(*hp)); 278 xwrite(fd, hp, sizeof(*hp));
297} 279}
298 280
281static void replace_symlink_placeholders(llist_t *list)
282{
283 while (list) {
284 char *target;
285
286 target = list->data + strlen(list->data) + 1;
287 if (symlink(target, list->data)) {
288 /* shared message */
289 bb_error_msg_and_die("can't create %slink '%s' to '%s'",
290 "sym",
291 list->data, target
292 );
293 }
294 list = list->link;
295 }
296}
297
299#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS 298#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
300static void writeLongname(int fd, int type, const char *name, int dir) 299static void writeLongname(int fd, int type, const char *name, int dir)
301{ 300{
@@ -1281,6 +1280,8 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
1281 while (get_header_tar(tar_handle) == EXIT_SUCCESS) 1280 while (get_header_tar(tar_handle) == EXIT_SUCCESS)
1282 bb_got_signal = EXIT_SUCCESS; /* saw at least one header, good */ 1281 bb_got_signal = EXIT_SUCCESS; /* saw at least one header, good */
1283 1282
1283 replace_symlink_placeholders(tar_handle->symlink_placeholders);
1284
1284 /* Check that every file that should have been extracted was */ 1285 /* Check that every file that should have been extracted was */
1285 while (tar_handle->accept) { 1286 while (tar_handle->accept) {
1286 if (!find_list_entry(tar_handle->reject, tar_handle->accept->data) 1287 if (!find_list_entry(tar_handle->reject, tar_handle->accept->data)
diff --git a/archival/tar_symlink_attack b/archival/tar_symlink_attack
new file mode 100755
index 000000000..35455f200
--- /dev/null
+++ b/archival/tar_symlink_attack
@@ -0,0 +1,16 @@
1#!/bin/sh
2# Makes "symlink attack" tarball (needs GNU tar for --append)
3
4true >anything.txt
5tar cvf tar_symlink_attack.tar anything.txt
6rm anything.txt
7
8ln -s /tmp symlink
9tar --append -f tar_symlink_attack.tar symlink
10rm symlink
11
12mkdir symlink
13echo BUG >symlink/bb_test_evilfile
14tar --append -f tar_symlink_attack.tar symlink/bb_test_evilfile
15rm symlink/bb_test_evilfile
16rmdir symlink
diff --git a/archival/unzip.c b/archival/unzip.c
index 4c4feda82..0dd18a75d 100644
--- a/archival/unzip.c
+++ b/archival/unzip.c
@@ -117,6 +117,7 @@ typedef union {
117 117
118#define FIX_ENDIANNESS_ZIP(zip) \ 118#define FIX_ENDIANNESS_ZIP(zip) \
119do { if (BB_BIG_ENDIAN) { \ 119do { if (BB_BIG_ENDIAN) { \
120 (zip).fmt.method = SWAP_LE16((zip).fmt.method ); \
120 (zip).fmt.crc32 = SWAP_LE32((zip).fmt.crc32 ); \ 121 (zip).fmt.crc32 = SWAP_LE32((zip).fmt.crc32 ); \
121 (zip).fmt.cmpsize = SWAP_LE32((zip).fmt.cmpsize ); \ 122 (zip).fmt.cmpsize = SWAP_LE32((zip).fmt.cmpsize ); \
122 (zip).fmt.ucmpsize = SWAP_LE32((zip).fmt.ucmpsize ); \ 123 (zip).fmt.ucmpsize = SWAP_LE32((zip).fmt.ucmpsize ); \
diff --git a/coreutils/link.c b/coreutils/link.c
index 56832fdf6..6e20dafe3 100644
--- a/coreutils/link.c
+++ b/coreutils/link.c
@@ -33,7 +33,7 @@ int link_main(int argc UNUSED_PARAM, char **argv)
33 if (link(argv[0], argv[1]) != 0) { 33 if (link(argv[0], argv[1]) != 0) {
34 /* shared message */ 34 /* shared message */
35 bb_perror_msg_and_die("can't create %slink " 35 bb_perror_msg_and_die("can't create %slink "
36 "%s to %s", "hard", 36 "'%s' to '%s'", "hard",
37 argv[1], argv[0] 37 argv[1], argv[0]
38 ); 38 );
39 } 39 }
diff --git a/editors/awk.c b/editors/awk.c
index 602a1d5e7..56688d72c 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -11,8 +11,7 @@
11//config: bool "awk (22 kb)" 11//config: bool "awk (22 kb)"
12//config: default y 12//config: default y
13//config: help 13//config: help
14//config: Awk is used as a pattern scanning and processing language. This is 14//config: Awk is used as a pattern scanning and processing language.
15//config: the BusyBox implementation of that programming language.
16//config: 15//config:
17//config:config FEATURE_AWK_LIBM 16//config:config FEATURE_AWK_LIBM
18//config: bool "Enable math functions (requires libm)" 17//config: bool "Enable math functions (requires libm)"
diff --git a/editors/ed.c b/editors/ed.c
index c594d3da1..7f21ded92 100644
--- a/editors/ed.c
+++ b/editors/ed.c
@@ -6,7 +6,6 @@
6 * 6 *
7 * The "ed" built-in command (much simplified) 7 * The "ed" built-in command (much simplified)
8 */ 8 */
9
10//config:config ED 9//config:config ED
11//config: bool "ed (25 kb)" 10//config: bool "ed (25 kb)"
12//config: default y 11//config: default y
@@ -19,7 +18,7 @@
19 18
20//applet:IF_ED(APPLET(ed, BB_DIR_BIN, BB_SUID_DROP)) 19//applet:IF_ED(APPLET(ed, BB_DIR_BIN, BB_SUID_DROP))
21 20
22//usage:#define ed_trivial_usage "" 21//usage:#define ed_trivial_usage "[FILE]"
23//usage:#define ed_full_usage "" 22//usage:#define ed_full_usage ""
24 23
25#include "libbb.h" 24#include "libbb.h"
@@ -32,7 +31,6 @@ typedef struct LINE {
32 char data[1]; 31 char data[1];
33} LINE; 32} LINE;
34 33
35
36#define searchString bb_common_bufsiz1 34#define searchString bb_common_bufsiz1
37 35
38enum { 36enum {
@@ -71,22 +69,6 @@ struct globals {
71 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 69 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
72} while (0) 70} while (0)
73 71
74
75static void doCommands(void);
76static void subCommand(const char *cmd, int num1, int num2);
77static int getNum(const char **retcp, smallint *retHaveNum, int *retNum);
78static int setCurNum(int num);
79static void addLines(int num);
80static int insertLine(int num, const char *data, int len);
81static void deleteLines(int num1, int num2);
82static int printLines(int num1, int num2, int expandFlag);
83static int writeLines(const char *file, int num1, int num2);
84static int readLines(const char *file, int num);
85static int searchLines(const char *str, int num1, int num2);
86static LINE *findLine(int num);
87static int findString(const LINE *lp, const char * str, int len, int offset);
88
89
90static int bad_nums(int num1, int num2, const char *for_what) 72static int bad_nums(int num1, int num2, const char *for_what)
91{ 73{
92 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { 74 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
@@ -96,421 +78,49 @@ static int bad_nums(int num1, int num2, const char *for_what)
96 return 0; 78 return 0;
97} 79}
98 80
99
100static char *skip_blank(const char *cp)
101{
102 while (isblank(*cp))
103 cp++;
104 return (char *)cp;
105}
106
107
108int ed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
109int ed_main(int argc UNUSED_PARAM, char **argv)
110{
111 INIT_G();
112
113 bufSize = INITBUF_SIZE;
114 bufBase = xmalloc(bufSize);
115 bufPtr = bufBase;
116 lines.next = &lines;
117 lines.prev = &lines;
118
119 if (argv[1]) {
120 fileName = xstrdup(argv[1]);
121 if (!readLines(fileName, 1)) {
122 return EXIT_SUCCESS;
123 }
124 if (lastNum)
125 setCurNum(1);
126 dirty = FALSE;
127 }
128
129 doCommands();
130 return EXIT_SUCCESS;
131}
132
133/*
134 * Read commands until we are told to stop.
135 */
136static void doCommands(void)
137{
138 const char *cp;
139 char *endbuf, buf[USERSIZE];
140 int len, num1, num2;
141 smallint have1, have2;
142
143 while (TRUE) {
144 /* Returns:
145 * -1 on read errors or EOF, or on bare Ctrl-D.
146 * 0 on ctrl-C,
147 * >0 length of input string, including terminating '\n'
148 */
149 len = read_line_input(NULL, ": ", buf, sizeof(buf), /*timeout*/ -1);
150 if (len <= 0)
151 return;
152 endbuf = &buf[len - 1];
153 while ((endbuf > buf) && isblank(endbuf[-1]))
154 endbuf--;
155 *endbuf = '\0';
156
157 cp = skip_blank(buf);
158 have1 = FALSE;
159 have2 = FALSE;
160
161 if ((curNum == 0) && (lastNum > 0)) {
162 curNum = 1;
163 curLine = lines.next;
164 }
165
166 if (!getNum(&cp, &have1, &num1))
167 continue;
168
169 cp = skip_blank(cp);
170
171 if (*cp == ',') {
172 cp++;
173 if (!getNum(&cp, &have2, &num2))
174 continue;
175 if (!have1)
176 num1 = 1;
177 if (!have2)
178 num2 = lastNum;
179 have1 = TRUE;
180 have2 = TRUE;
181 }
182 if (!have1)
183 num1 = curNum;
184 if (!have2)
185 num2 = num1;
186
187 switch (*cp++) {
188 case 'a':
189 addLines(num1 + 1);
190 break;
191
192 case 'c':
193 deleteLines(num1, num2);
194 addLines(num1);
195 break;
196
197 case 'd':
198 deleteLines(num1, num2);
199 break;
200
201 case 'f':
202 if (*cp && !isblank(*cp)) {
203 bb_error_msg("bad file command");
204 break;
205 }
206 cp = skip_blank(cp);
207 if (*cp == '\0') {
208 if (fileName)
209 printf("\"%s\"\n", fileName);
210 else
211 puts("No file name");
212 break;
213 }
214 free(fileName);
215 fileName = xstrdup(cp);
216 break;
217
218 case 'i':
219 addLines(num1);
220 break;
221
222 case 'k':
223 cp = skip_blank(cp);
224 if ((*cp < 'a') || (*cp > 'z') || cp[1]) {
225 bb_error_msg("bad mark name");
226 break;
227 }
228 marks[*cp - 'a'] = num2;
229 break;
230
231 case 'l':
232 printLines(num1, num2, TRUE);
233 break;
234
235 case 'p':
236 printLines(num1, num2, FALSE);
237 break;
238
239 case 'q':
240 cp = skip_blank(cp);
241 if (have1 || *cp) {
242 bb_error_msg("bad quit command");
243 break;
244 }
245 if (!dirty)
246 return;
247 len = read_line_input(NULL, "Really quit? ", buf, 16, /*timeout*/ -1);
248 /* read error/EOF - no way to continue */
249 if (len < 0)
250 return;
251 cp = skip_blank(buf);
252 if ((*cp | 0x20) == 'y') /* Y or y */
253 return;
254 break;
255
256 case 'r':
257 if (*cp && !isblank(*cp)) {
258 bb_error_msg("bad read command");
259 break;
260 }
261 cp = skip_blank(cp);
262 if (*cp == '\0') {
263 bb_error_msg("no file name");
264 break;
265 }
266 if (!have1)
267 num1 = lastNum;
268 if (readLines(cp, num1 + 1))
269 break;
270 if (fileName == NULL)
271 fileName = xstrdup(cp);
272 break;
273
274 case 's':
275 subCommand(cp, num1, num2);
276 break;
277
278 case 'w':
279 if (*cp && !isblank(*cp)) {
280 bb_error_msg("bad write command");
281 break;
282 }
283 cp = skip_blank(cp);
284 if (!have1) {
285 num1 = 1;
286 num2 = lastNum;
287 }
288 if (*cp == '\0')
289 cp = fileName;
290 if (cp == NULL) {
291 bb_error_msg("no file name specified");
292 break;
293 }
294 writeLines(cp, num1, num2);
295 break;
296
297 case 'z':
298 switch (*cp) {
299 case '-':
300 printLines(curNum - 21, curNum, FALSE);
301 break;
302 case '.':
303 printLines(curNum - 11, curNum + 10, FALSE);
304 break;
305 default:
306 printLines(curNum, curNum + 21, FALSE);
307 break;
308 }
309 break;
310
311 case '.':
312 if (have1) {
313 bb_error_msg("no arguments allowed");
314 break;
315 }
316 printLines(curNum, curNum, FALSE);
317 break;
318
319 case '-':
320 if (setCurNum(curNum - 1))
321 printLines(curNum, curNum, FALSE);
322 break;
323
324 case '=':
325 printf("%d\n", num1);
326 break;
327 case '\0':
328 if (have1) {
329 printLines(num2, num2, FALSE);
330 break;
331 }
332 if (setCurNum(curNum + 1))
333 printLines(curNum, curNum, FALSE);
334 break;
335
336 default:
337 bb_error_msg("unimplemented command");
338 break;
339 }
340 }
341}
342
343
344/* 81/*
345 * Do the substitute command. 82 * Return a pointer to the specified line number.
346 * The current line is set to the last substitution done.
347 */ 83 */
348static void subCommand(const char *cmd, int num1, int num2) 84static LINE *findLine(int num)
349{ 85{
350 char *cp, *oldStr, *newStr, buf[USERSIZE]; 86 LINE *lp;
351 int delim, oldLen, newLen, deltaLen, offset; 87 int lnum;
352 LINE *lp, *nlp;
353 int globalFlag, printFlag, didSub, needPrint;
354
355 if (bad_nums(num1, num2, "substitute"))
356 return;
357
358 globalFlag = FALSE;
359 printFlag = FALSE;
360 didSub = FALSE;
361 needPrint = FALSE;
362
363 /*
364 * Copy the command so we can modify it.
365 */
366 strcpy(buf, cmd);
367 cp = buf;
368 88
369 if (isblank(*cp) || (*cp == '\0')) { 89 if ((num < 1) || (num > lastNum)) {
370 bb_error_msg("bad delimiter for substitute"); 90 bb_error_msg("line number %d does not exist", num);
371 return; 91 return NULL;
372 } 92 }
373 93
374 delim = *cp++; 94 if (curNum <= 0) {
375 oldStr = cp; 95 curNum = 1;
376 96 curLine = lines.next;
377 cp = strchr(cp, delim);
378 if (cp == NULL) {
379 bb_error_msg("missing 2nd delimiter for substitute");
380 return;
381 } 97 }
382 98
383 *cp++ = '\0'; 99 if (num == curNum)
384 100 return curLine;
385 newStr = cp;
386 cp = strchr(cp, delim);
387
388 if (cp)
389 *cp++ = '\0';
390 else
391 cp = (char*)"";
392
393 while (*cp) switch (*cp++) {
394 case 'g':
395 globalFlag = TRUE;
396 break;
397 case 'p':
398 printFlag = TRUE;
399 break;
400 default:
401 bb_error_msg("unknown option for substitute");
402 return;
403 }
404 101
405 if (*oldStr == '\0') { 102 lp = curLine;
406 if (searchString[0] == '\0') { 103 lnum = curNum;
407 bb_error_msg("no previous search string"); 104 if (num < (curNum / 2)) {
408 return; 105 lp = lines.next;
409 } 106 lnum = 1;
410 oldStr = searchString; 107 } else if (num > ((curNum + lastNum) / 2)) {
108 lp = lines.prev;
109 lnum = lastNum;
411 } 110 }
412 111
413 if (oldStr != searchString) 112 while (lnum < num) {
414 strcpy(searchString, oldStr);
415
416 lp = findLine(num1);
417 if (lp == NULL)
418 return;
419
420 oldLen = strlen(oldStr);
421 newLen = strlen(newStr);
422 deltaLen = newLen - oldLen;
423 offset = 0;
424 nlp = NULL;
425
426 while (num1 <= num2) {
427 offset = findString(lp, oldStr, oldLen, offset);
428
429 if (offset < 0) {
430 if (needPrint) {
431 printLines(num1, num1, FALSE);
432 needPrint = FALSE;
433 }
434 offset = 0;
435 lp = lp->next;
436 num1++;
437 continue;
438 }
439
440 needPrint = printFlag;
441 didSub = TRUE;
442 dirty = TRUE;
443
444 /*
445 * If the replacement string is the same size or shorter
446 * than the old string, then the substitution is easy.
447 */
448 if (deltaLen <= 0) {
449 memcpy(&lp->data[offset], newStr, newLen);
450 if (deltaLen) {
451 memcpy(&lp->data[offset + newLen],
452 &lp->data[offset + oldLen],
453 lp->len - offset - oldLen);
454
455 lp->len += deltaLen;
456 }
457 offset += newLen;
458 if (globalFlag)
459 continue;
460 if (needPrint) {
461 printLines(num1, num1, FALSE);
462 needPrint = FALSE;
463 }
464 lp = lp->next;
465 num1++;
466 continue;
467 }
468
469 /*
470 * The new string is larger, so allocate a new line
471 * structure and use that. Link it in place of
472 * the old line structure.
473 */
474 nlp = xmalloc(sizeof(LINE) + lp->len + deltaLen);
475
476 nlp->len = lp->len + deltaLen;
477
478 memcpy(nlp->data, lp->data, offset);
479 memcpy(&nlp->data[offset], newStr, newLen);
480 memcpy(&nlp->data[offset + newLen],
481 &lp->data[offset + oldLen],
482 lp->len - offset - oldLen);
483
484 nlp->next = lp->next;
485 nlp->prev = lp->prev;
486 nlp->prev->next = nlp;
487 nlp->next->prev = nlp;
488
489 if (curLine == lp)
490 curLine = nlp;
491
492 free(lp);
493 lp = nlp;
494
495 offset += newLen;
496
497 if (globalFlag)
498 continue;
499
500 if (needPrint) {
501 printLines(num1, num1, FALSE);
502 needPrint = FALSE;
503 }
504
505 lp = lp->next; 113 lp = lp->next;
506 num1++; 114 lnum++;
507 } 115 }
508 116
509 if (!didSub) 117 while (lnum > num) {
510 bb_error_msg("no substitutions found for \"%s\"", oldStr); 118 lp = lp->prev;
119 lnum--;
120 }
121 return lp;
511} 122}
512 123
513
514/* 124/*
515 * Search a line for the specified string starting at the specified 125 * Search a line for the specified string starting at the specified
516 * offset in the line. Returns the offset of the found string, or -1. 126 * offset in the line. Returns the offset of the found string, or -1.
@@ -521,15 +131,13 @@ static int findString(const LINE *lp, const char *str, int len, int offset)
521 const char *cp, *ncp; 131 const char *cp, *ncp;
522 132
523 cp = &lp->data[offset]; 133 cp = &lp->data[offset];
524 left = lp->len - offset; 134 left = lp->len - offset - len;
525 135
526 while (left >= len) { 136 while (left >= 0) {
527 ncp = memchr(cp, *str, left); 137 ncp = memchr(cp, str[0], left + 1);
528 if (ncp == NULL) 138 if (ncp == NULL)
529 return -1; 139 return -1;
530 left -= (ncp - cp); 140 left -= (ncp - cp);
531 if (left < len)
532 return -1;
533 cp = ncp; 141 cp = ncp;
534 if (memcmp(cp, str, len) == 0) 142 if (memcmp(cp, str, len) == 0)
535 return (cp - lp->data); 143 return (cp - lp->data);
@@ -540,60 +148,69 @@ static int findString(const LINE *lp, const char *str, int len, int offset)
540 return -1; 148 return -1;
541} 149}
542 150
543
544/* 151/*
545 * Add lines which are typed in by the user. 152 * Search for a line which contains the specified string.
546 * The lines are inserted just before the specified line number. 153 * If the string is "", then the previously searched for string
547 * The lines are terminated by a line containing a single dot (ugly!), 154 * is used. The currently searched for string is saved for future use.
548 * or by an end of file. 155 * Returns the line number which matches, or 0 if there was no match
156 * with an error printed.
549 */ 157 */
550static void addLines(int num) 158static NOINLINE int searchLines(const char *str, int num1, int num2)
551{ 159{
160 const LINE *lp;
552 int len; 161 int len;
553 char buf[USERSIZE + 1];
554 162
555 while (1) { 163 if (bad_nums(num1, num2, "search"))
556 /* Returns: 164 return 0;
557 * -1 on read errors or EOF, or on bare Ctrl-D. 165
558 * 0 on ctrl-C, 166 if (*str == '\0') {
559 * >0 length of input string, including terminating '\n' 167 if (searchString[0] == '\0') {
560 */ 168 bb_error_msg("no previous search string");
561 len = read_line_input(NULL, "", buf, sizeof(buf), /*timeout*/ -1); 169 return 0;
562 if (len <= 0) {
563 /* Previously, ctrl-C was exiting to shell.
564 * Now we exit to ed prompt. Is in important? */
565 return;
566 } 170 }
567 if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0')) 171 str = searchString;
568 return;
569 if (!insertLine(num++, buf, len))
570 return;
571 } 172 }
572}
573 173
174 if (str != searchString)
175 strcpy(searchString, str);
176
177 len = strlen(str);
178
179 lp = findLine(num1);
180 if (lp == NULL)
181 return 0;
182
183 while (num1 <= num2) {
184 if (findString(lp, str, len, 0) >= 0)
185 return num1;
186 num1++;
187 lp = lp->next;
188 }
189
190 bb_error_msg("can't find string \"%s\"", str);
191 return 0;
192}
574 193
575/* 194/*
576 * Parse a line number argument if it is present. This is a sum 195 * Parse a line number argument if it is present. This is a sum
577 * or difference of numbers, '.', '$', 'x, or a search string. 196 * or difference of numbers, ".", "$", "'c", or a search string.
578 * Returns TRUE if successful (whether or not there was a number). 197 * Returns pointer which stopped the scan if successful
579 * Returns FALSE if there was a parsing error, with a message output. 198 * (whether or not there was a number).
199 * Returns NULL if there was a parsing error, with a message output.
580 * Whether there was a number is returned indirectly, as is the number. 200 * Whether there was a number is returned indirectly, as is the number.
581 * The character pointer which stopped the scan is also returned.
582 */ 201 */
583static int getNum(const char **retcp, smallint *retHaveNum, int *retNum) 202static const char* getNum(const char *cp, smallint *retHaveNum, int *retNum)
584{ 203{
585 const char *cp;
586 char *endStr, str[USERSIZE]; 204 char *endStr, str[USERSIZE];
587 int value, num; 205 int value, num;
588 smallint haveNum, minus; 206 smallint haveNum, minus;
589 207
590 cp = *retcp;
591 value = 0; 208 value = 0;
592 haveNum = FALSE; 209 haveNum = FALSE;
593 minus = 0; 210 minus = 0;
594 211
595 while (TRUE) { 212 while (TRUE) {
596 cp = skip_blank(cp); 213 cp = skip_whitespace(cp);
597 214
598 switch (*cp) { 215 switch (*cp) {
599 case '.': 216 case '.':
@@ -610,12 +227,13 @@ static int getNum(const char **retcp, smallint *retHaveNum, int *retNum)
610 227
611 case '\'': 228 case '\'':
612 cp++; 229 cp++;
613 if ((*cp < 'a') || (*cp > 'z')) { 230 if ((unsigned)(*cp - 'a') >= 26) {
614 bb_error_msg("bad mark name"); 231 bb_error_msg("bad mark name");
615 return FALSE; 232 return NULL;
616 } 233 }
617 haveNum = TRUE; 234 haveNum = TRUE;
618 num = marks[*cp++ - 'a']; 235 num = marks[(unsigned)(*cp - 'a')];
236 cp++;
619 break; 237 break;
620 238
621 case '/': 239 case '/':
@@ -628,16 +246,15 @@ static int getNum(const char **retcp, smallint *retHaveNum, int *retNum)
628 cp = ""; 246 cp = "";
629 num = searchLines(str, curNum, lastNum); 247 num = searchLines(str, curNum, lastNum);
630 if (num == 0) 248 if (num == 0)
631 return FALSE; 249 return NULL;
632 haveNum = TRUE; 250 haveNum = TRUE;
633 break; 251 break;
634 252
635 default: 253 default:
636 if (!isdigit(*cp)) { 254 if (!isdigit(*cp)) {
637 *retcp = cp;
638 *retHaveNum = haveNum; 255 *retHaveNum = haveNum;
639 *retNum = value; 256 *retNum = value;
640 return TRUE; 257 return cp;
641 } 258 }
642 num = 0; 259 num = 0;
643 while (isdigit(*cp)) 260 while (isdigit(*cp))
@@ -648,7 +265,7 @@ static int getNum(const char **retcp, smallint *retHaveNum, int *retNum)
648 265
649 value += (minus ? -num : num); 266 value += (minus ? -num : num);
650 267
651 cp = skip_blank(cp); 268 cp = skip_whitespace(cp);
652 269
653 switch (*cp) { 270 switch (*cp) {
654 case '-': 271 case '-':
@@ -662,14 +279,99 @@ static int getNum(const char **retcp, smallint *retHaveNum, int *retNum)
662 break; 279 break;
663 280
664 default: 281 default:
665 *retcp = cp;
666 *retHaveNum = haveNum; 282 *retHaveNum = haveNum;
667 *retNum = value; 283 *retNum = value;
668 return TRUE; 284 return cp;
669 } 285 }
670 } 286 }
671} 287}
672 288
289/*
290 * Set the current line number.
291 * Returns TRUE if successful.
292 */
293static int setCurNum(int num)
294{
295 LINE *lp;
296
297 lp = findLine(num);
298 if (lp == NULL)
299 return FALSE;
300 curNum = num;
301 curLine = lp;
302 return TRUE;
303}
304
305/*
306 * Insert a new line with the specified text.
307 * The line is inserted so as to become the specified line,
308 * thus pushing any existing and further lines down one.
309 * The inserted line is also set to become the current line.
310 * Returns TRUE if successful.
311 */
312static int insertLine(int num, const char *data, int len)
313{
314 LINE *newLp, *lp;
315
316 if ((num < 1) || (num > lastNum + 1)) {
317 bb_error_msg("inserting at bad line number");
318 return FALSE;
319 }
320
321 newLp = xmalloc(sizeof(LINE) + len - 1);
322
323 memcpy(newLp->data, data, len);
324 newLp->len = len;
325
326 if (num > lastNum)
327 lp = &lines;
328 else {
329 lp = findLine(num);
330 if (lp == NULL) {
331 free((char *) newLp);
332 return FALSE;
333 }
334 }
335
336 newLp->next = lp;
337 newLp->prev = lp->prev;
338 lp->prev->next = newLp;
339 lp->prev = newLp;
340
341 lastNum++;
342 dirty = TRUE;
343 return setCurNum(num);
344}
345
346/*
347 * Add lines which are typed in by the user.
348 * The lines are inserted just before the specified line number.
349 * The lines are terminated by a line containing a single dot (ugly!),
350 * or by an end of file.
351 */
352static void addLines(int num)
353{
354 int len;
355 char buf[USERSIZE + 1];
356
357 while (1) {
358 /* Returns:
359 * -1 on read errors or EOF, or on bare Ctrl-D.
360 * 0 on ctrl-C,
361 * >0 length of input string, including terminating '\n'
362 */
363 len = read_line_input(NULL, "", buf, sizeof(buf), /*timeout*/ -1);
364 if (len <= 0) {
365 /* Previously, ctrl-C was exiting to shell.
366 * Now we exit to ed prompt. Is in important? */
367 return;
368 }
369 if (buf[0] == '.' && buf[1] == '\n' && buf[2] == '\0')
370 return;
371 if (!insertLine(num++, buf, len))
372 return;
373 }
374}
673 375
674/* 376/*
675 * Read lines from a file at the specified line number. 377 * Read lines from a file at the specified line number.
@@ -759,7 +461,6 @@ static int readLines(const char *file, int num)
759 return TRUE; 461 return TRUE;
760} 462}
761 463
762
763/* 464/*
764 * Write the specified lines out to the specified file. 465 * Write the specified lines out to the specified file.
765 * Returns TRUE if successful, or FALSE on an error with a message output. 466 * Returns TRUE if successful, or FALSE on an error with a message output.
@@ -810,7 +511,6 @@ static int writeLines(const char *file, int num1, int num2)
810 return TRUE; 511 return TRUE;
811} 512}
812 513
813
814/* 514/*
815 * Print lines in a specified range. 515 * Print lines in a specified range.
816 * The last line printed becomes the current line. 516 * The last line printed becomes the current line.
@@ -862,49 +562,6 @@ static int printLines(int num1, int num2, int expandFlag)
862 return TRUE; 562 return TRUE;
863} 563}
864 564
865
866/*
867 * Insert a new line with the specified text.
868 * The line is inserted so as to become the specified line,
869 * thus pushing any existing and further lines down one.
870 * The inserted line is also set to become the current line.
871 * Returns TRUE if successful.
872 */
873static int insertLine(int num, const char *data, int len)
874{
875 LINE *newLp, *lp;
876
877 if ((num < 1) || (num > lastNum + 1)) {
878 bb_error_msg("inserting at bad line number");
879 return FALSE;
880 }
881
882 newLp = xmalloc(sizeof(LINE) + len - 1);
883
884 memcpy(newLp->data, data, len);
885 newLp->len = len;
886
887 if (num > lastNum)
888 lp = &lines;
889 else {
890 lp = findLine(num);
891 if (lp == NULL) {
892 free((char *) newLp);
893 return FALSE;
894 }
895 }
896
897 newLp->next = lp;
898 newLp->prev = lp->prev;
899 lp->prev->next = newLp;
900 lp->prev = newLp;
901
902 lastNum++;
903 dirty = TRUE;
904 return setCurNum(num);
905}
906
907
908/* 565/*
909 * Delete lines from the given range. 566 * Delete lines from the given range.
910 */ 567 */
@@ -946,107 +603,414 @@ static void deleteLines(int num1, int num2)
946 dirty = TRUE; 603 dirty = TRUE;
947} 604}
948 605
949
950/* 606/*
951 * Search for a line which contains the specified string. 607 * Do the substitute command.
952 * If the string is "", then the previously searched for string 608 * The current line is set to the last substitution done.
953 * is used. The currently searched for string is saved for future use.
954 * Returns the line number which matches, or 0 if there was no match
955 * with an error printed.
956 */ 609 */
957static NOINLINE int searchLines(const char *str, int num1, int num2) 610static void subCommand(const char *cmd, int num1, int num2)
958{ 611{
959 const LINE *lp; 612 char *cp, *oldStr, *newStr, buf[USERSIZE];
960 int len; 613 int delim, oldLen, newLen, deltaLen, offset;
614 LINE *lp, *nlp;
615 int globalFlag, printFlag, didSub, needPrint;
961 616
962 if (bad_nums(num1, num2, "search")) 617 if (bad_nums(num1, num2, "substitute"))
963 return 0; 618 return;
964 619
965 if (*str == '\0') { 620 globalFlag = FALSE;
621 printFlag = FALSE;
622 didSub = FALSE;
623 needPrint = FALSE;
624
625 /*
626 * Copy the command so we can modify it.
627 */
628 strcpy(buf, cmd);
629 cp = buf;
630
631 if (isblank(*cp) || (*cp == '\0')) {
632 bb_error_msg("bad delimiter for substitute");
633 return;
634 }
635
636 delim = *cp++;
637 oldStr = cp;
638
639 cp = strchr(cp, delim);
640 if (cp == NULL) {
641 bb_error_msg("missing 2nd delimiter for substitute");
642 return;
643 }
644
645 *cp++ = '\0';
646
647 newStr = cp;
648 cp = strchr(cp, delim);
649
650 if (cp)
651 *cp++ = '\0';
652 else
653 cp = (char*)"";
654
655 while (*cp) switch (*cp++) {
656 case 'g':
657 globalFlag = TRUE;
658 break;
659 case 'p':
660 printFlag = TRUE;
661 break;
662 default:
663 bb_error_msg("unknown option for substitute");
664 return;
665 }
666
667 if (*oldStr == '\0') {
966 if (searchString[0] == '\0') { 668 if (searchString[0] == '\0') {
967 bb_error_msg("no previous search string"); 669 bb_error_msg("no previous search string");
968 return 0; 670 return;
969 } 671 }
970 str = searchString; 672 oldStr = searchString;
971 } 673 }
972 674
973 if (str != searchString) 675 if (oldStr != searchString)
974 strcpy(searchString, str); 676 strcpy(searchString, oldStr);
975
976 len = strlen(str);
977 677
978 lp = findLine(num1); 678 lp = findLine(num1);
979 if (lp == NULL) 679 if (lp == NULL)
980 return 0; 680 return;
681
682 oldLen = strlen(oldStr);
683 newLen = strlen(newStr);
684 deltaLen = newLen - oldLen;
685 offset = 0;
686 nlp = NULL;
981 687
982 while (num1 <= num2) { 688 while (num1 <= num2) {
983 if (findString(lp, str, len, 0) >= 0) 689 offset = findString(lp, oldStr, oldLen, offset);
984 return num1; 690
985 num1++; 691 if (offset < 0) {
692 if (needPrint) {
693 printLines(num1, num1, FALSE);
694 needPrint = FALSE;
695 }
696 offset = 0;
697 lp = lp->next;
698 num1++;
699 continue;
700 }
701
702 needPrint = printFlag;
703 didSub = TRUE;
704 dirty = TRUE;
705
706 /*
707 * If the replacement string is the same size or shorter
708 * than the old string, then the substitution is easy.
709 */
710 if (deltaLen <= 0) {
711 memcpy(&lp->data[offset], newStr, newLen);
712 if (deltaLen) {
713 memcpy(&lp->data[offset + newLen],
714 &lp->data[offset + oldLen],
715 lp->len - offset - oldLen);
716
717 lp->len += deltaLen;
718 }
719 offset += newLen;
720 if (globalFlag)
721 continue;
722 if (needPrint) {
723 printLines(num1, num1, FALSE);
724 needPrint = FALSE;
725 }
726 lp = lp->next;
727 num1++;
728 continue;
729 }
730
731 /*
732 * The new string is larger, so allocate a new line
733 * structure and use that. Link it in place of
734 * the old line structure.
735 */
736 nlp = xmalloc(sizeof(LINE) + lp->len + deltaLen);
737
738 nlp->len = lp->len + deltaLen;
739
740 memcpy(nlp->data, lp->data, offset);
741 memcpy(&nlp->data[offset], newStr, newLen);
742 memcpy(&nlp->data[offset + newLen],
743 &lp->data[offset + oldLen],
744 lp->len - offset - oldLen);
745
746 nlp->next = lp->next;
747 nlp->prev = lp->prev;
748 nlp->prev->next = nlp;
749 nlp->next->prev = nlp;
750
751 if (curLine == lp)
752 curLine = nlp;
753
754 free(lp);
755 lp = nlp;
756
757 offset += newLen;
758
759 if (globalFlag)
760 continue;
761
762 if (needPrint) {
763 printLines(num1, num1, FALSE);
764 needPrint = FALSE;
765 }
766
986 lp = lp->next; 767 lp = lp->next;
768 num1++;
987 } 769 }
988 770
989 bb_error_msg("can't find string \"%s\"", str); 771 if (!didSub)
990 return 0; 772 bb_error_msg("no substitutions found for \"%s\"", oldStr);
991} 773}
992 774
993
994/* 775/*
995 * Return a pointer to the specified line number. 776 * Read commands until we are told to stop.
996 */ 777 */
997static LINE *findLine(int num) 778static void doCommands(void)
998{ 779{
999 LINE *lp; 780 while (TRUE) {
1000 int lnum; 781 char buf[USERSIZE];
782 const char *cp;
783 int len;
784 int n, num1, num2;
785 smallint h, have1, have2;
1001 786
1002 if ((num < 1) || (num > lastNum)) { 787 /* Returns:
1003 bb_error_msg("line number %d does not exist", num); 788 * -1 on read errors or EOF, or on bare Ctrl-D.
1004 return NULL; 789 * 0 on ctrl-C,
1005 } 790 * >0 length of input string, including terminating '\n'
791 */
792 len = read_line_input(NULL, ": ", buf, sizeof(buf), /*timeout*/ -1);
793 if (len <= 0)
794 return;
795 while (len && isspace(buf[--len]))
796 buf[len] = '\0';
1006 797
1007 if (curNum <= 0) { 798 if ((curNum == 0) && (lastNum > 0)) {
1008 curNum = 1; 799 curNum = 1;
1009 curLine = lines.next; 800 curLine = lines.next;
1010 } 801 }
1011 802
1012 if (num == curNum) 803 have1 = FALSE;
1013 return curLine; 804 have2 = FALSE;
805 /* Don't pass &haveN, &numN to getNum() since this forces
806 * compiler to keep them on stack, not in registers,
807 * which is usually quite suboptimal.
808 * Using intermediate variables shrinks code by ~150 bytes.
809 */
810 cp = getNum(skip_whitespace(buf), &h, &n);
811 if (!cp)
812 continue;
813 have1 = h;
814 num1 = n;
815 cp = skip_whitespace(cp);
816 if (*cp == ',') {
817 cp = getNum(cp + 1, &h, &n);
818 if (!cp)
819 continue;
820 num2 = n;
821 if (!have1)
822 num1 = 1;
823 if (!h)
824 num2 = lastNum;
825 have1 = TRUE;
826 have2 = TRUE;
827 }
828 if (!have1)
829 num1 = curNum;
830 if (!have2)
831 num2 = num1;
1014 832
1015 lp = curLine; 833 switch (*cp++) {
1016 lnum = curNum; 834 case 'a':
1017 if (num < (curNum / 2)) { 835 addLines(num1 + 1);
1018 lp = lines.next; 836 break;
1019 lnum = 1;
1020 } else if (num > ((curNum + lastNum) / 2)) {
1021 lp = lines.prev;
1022 lnum = lastNum;
1023 }
1024 837
1025 while (lnum < num) { 838 case 'c':
1026 lp = lp->next; 839 deleteLines(num1, num2);
1027 lnum++; 840 addLines(num1);
1028 } 841 break;
1029 842
1030 while (lnum > num) { 843 case 'd':
1031 lp = lp->prev; 844 deleteLines(num1, num2);
1032 lnum--; 845 break;
846
847 case 'f':
848 if (*cp != '\0' && *cp != ' ') {
849 bb_error_msg("bad file command");
850 break;
851 }
852 cp = skip_whitespace(cp);
853 if (*cp == '\0') {
854 if (fileName)
855 printf("\"%s\"\n", fileName);
856 else
857 puts("No file name");
858 break;
859 }
860 free(fileName);
861 fileName = xstrdup(cp);
862 break;
863
864 case 'i':
865 if (!have1 && lastNum == 0)
866 num1 = 1;
867 addLines(num1);
868 break;
869
870 case 'k':
871 cp = skip_whitespace(cp);
872 if ((unsigned)(*cp - 'a') >= 26 || cp[1]) {
873 bb_error_msg("bad mark name");
874 break;
875 }
876 marks[(unsigned)(*cp - 'a')] = num2;
877 break;
878
879 case 'l':
880 printLines(num1, num2, TRUE);
881 break;
882
883 case 'p':
884 printLines(num1, num2, FALSE);
885 break;
886
887 case 'q':
888 cp = skip_whitespace(cp);
889 if (have1 || *cp) {
890 bb_error_msg("bad quit command");
891 break;
892 }
893 if (!dirty)
894 return;
895 len = read_line_input(NULL, "Really quit? ", buf, 16, /*timeout*/ -1);
896 /* read error/EOF - no way to continue */
897 if (len < 0)
898 return;
899 cp = skip_whitespace(buf);
900 if ((*cp | 0x20) == 'y') /* Y or y */
901 return;
902 break;
903
904 case 'r':
905 if (*cp != '\0' && *cp != ' ') {
906 bb_error_msg("bad read command");
907 break;
908 }
909 cp = skip_whitespace(cp);
910 if (*cp == '\0') {
911 bb_error_msg("no file name");
912 break;
913 }
914 if (!have1)
915 num1 = lastNum;
916 if (readLines(cp, num1 + 1))
917 break;
918 if (fileName == NULL)
919 fileName = xstrdup(cp);
920 break;
921
922 case 's':
923 subCommand(cp, num1, num2);
924 break;
925
926 case 'w':
927 if (*cp != '\0' && *cp != ' ') {
928 bb_error_msg("bad write command");
929 break;
930 }
931 cp = skip_whitespace(cp);
932 if (*cp == '\0') {
933 cp = fileName;
934 if (!cp) {
935 bb_error_msg("no file name specified");
936 break;
937 }
938 }
939 if (!have1) {
940 num1 = 1;
941 num2 = lastNum;
942 dirty = FALSE;
943 }
944 writeLines(cp, num1, num2);
945 break;
946
947 case 'z':
948 switch (*cp) {
949 case '-':
950 printLines(curNum - 21, curNum, FALSE);
951 break;
952 case '.':
953 printLines(curNum - 11, curNum + 10, FALSE);
954 break;
955 default:
956 printLines(curNum, curNum + 21, FALSE);
957 break;
958 }
959 break;
960
961 case '.':
962 if (have1) {
963 bb_error_msg("no arguments allowed");
964 break;
965 }
966 printLines(curNum, curNum, FALSE);
967 break;
968
969 case '-':
970 if (setCurNum(curNum - 1))
971 printLines(curNum, curNum, FALSE);
972 break;
973
974 case '=':
975 printf("%d\n", num1);
976 break;
977 case '\0':
978 if (have1) {
979 printLines(num2, num2, FALSE);
980 break;
981 }
982 if (setCurNum(curNum + 1))
983 printLines(curNum, curNum, FALSE);
984 break;
985
986 default:
987 bb_error_msg("unimplemented command");
988 break;
989 }
1033 } 990 }
1034 return lp;
1035} 991}
1036 992
1037 993int ed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1038/* 994int ed_main(int argc UNUSED_PARAM, char **argv)
1039 * Set the current line number.
1040 * Returns TRUE if successful.
1041 */
1042static int setCurNum(int num)
1043{ 995{
1044 LINE *lp; 996 INIT_G();
1045 997
1046 lp = findLine(num); 998 bufSize = INITBUF_SIZE;
1047 if (lp == NULL) 999 bufBase = xmalloc(bufSize);
1048 return FALSE; 1000 bufPtr = bufBase;
1049 curNum = num; 1001 lines.next = &lines;
1050 curLine = lp; 1002 lines.prev = &lines;
1051 return TRUE; 1003
1004 if (argv[1]) {
1005 fileName = xstrdup(argv[1]);
1006 if (!readLines(fileName, 1)) {
1007 return EXIT_SUCCESS;
1008 }
1009 if (lastNum)
1010 setCurNum(1);
1011 dirty = FALSE;
1012 }
1013
1014 doCommands();
1015 return EXIT_SUCCESS;
1052} 1016}
diff --git a/examples/var_service/README b/examples/var_service/README
index 938cce91d..15a1bc9d2 100644
--- a/examples/var_service/README
+++ b/examples/var_service/README
@@ -98,7 +98,7 @@ restart) each other.
98 98
99 var_service/dhcp_if 99 var_service/dhcp_if
100 100
101controls a udhcpc instance which provides dhpc-assigned IP 101controls a udhcpc instance which provides DHCP-assigned IP
102address on interface named "if". Copy/rename this directory as needed to run 102address on interface named "if". Copy/rename this directory as needed to run
103udhcpc on other interfaces (var_service/dhcp_if/run script uses _foo suffix 103udhcpc on other interfaces (var_service/dhcp_if/run script uses _foo suffix
104of the parent directory as interface name). 104of the parent directory as interface name).
@@ -164,9 +164,9 @@ This is achieved very simply by having
164 sv o . 164 sv o .
165at the very beginning of fw/run script, not at the end. 165at the very beginning of fw/run script, not at the end.
166 166
167Therefore, any "sv u /var/run/service/fw" command by any other 167Therefore, any "sv u fw" command by any other script "undoes" o(ne-shot)
168script "undoes" o(ne-shot) command if fw still runs, thus 168command if fw still runs, thus runsv will rerun it; or start it
169runsv will rerun it; or start it in a normal way if fw is not running. 169in a normal way if fw is not running.
170 170
171This mechanism is the reason why fw is a service, not just a script. 171This mechanism is the reason why fw is a service, not just a script.
172 172
@@ -198,7 +198,8 @@ PID TIME COMMAND
198568 0:00 svlogd -tt /var/log/service/dhcp_eth0 198568 0:00 svlogd -tt /var/log/service/dhcp_eth0
199850 0:00 udhcpc -vv --foreground --interface=eth0 199850 0:00 udhcpc -vv --foreground --interface=eth0
200 --pidfile=/var/service/dhcp_eth0/udhcpc.pid 200 --pidfile=/var/service/dhcp_eth0/udhcpc.pid
201 --script=/var/service/dhcp_eth0/dhcp_handler -x hostname bbox 201 --script=/var/service/dhcp_eth0/dhcp_handler
202 -x hostname bbox
202563 0:00 runsv ntpd 203563 0:00 runsv ntpd
203573 0:01 svlogd -tt /var/log/service/ntpd 204573 0:01 svlogd -tt /var/log/service/ntpd
204845 0:00 busybox ntpd -dddnNl -S ./ntp.script -p 10.x.x.x -p 10.x.x.x 205845 0:00 busybox ntpd -dddnNl -S ./ntp.script -p 10.x.x.x -p 10.x.x.x
@@ -233,4 +234,5 @@ PID TIME COMMAND
233622 0:00 busybox httpd -p80 -vvv -f -h /home/httpd_root 234622 0:00 busybox httpd -p80 -vvv -f -h /home/httpd_root
234577 0:00 runsv supplicant_wlan0 235577 0:00 runsv supplicant_wlan0
235627 0:00 svlogd -tt /var/log/service/supplicant_wlan0 236627 0:00 svlogd -tt /var/log/service/supplicant_wlan0
236638 0:03 wpa_supplicant -i wlan0 -c /var/service/supplicant_wlan0/wpa_supplicant.conf -d 237638 0:03 wpa_supplicant -i wlan0
238 -c /var/service/supplicant_wlan0/wpa_supplicant.conf -d
diff --git a/examples/var_service/README_distro_proposal.txt b/examples/var_service/README_distro_proposal.txt
index 9ba952cb4..ec887b4e1 100644
--- a/examples/var_service/README_distro_proposal.txt
+++ b/examples/var_service/README_distro_proposal.txt
@@ -90,9 +90,9 @@ There are several reimplementations of daemontools:
90 (busybox has it included) 90 (busybox has it included)
91- s6: by Laurent Bercot, http://skarnet.org/software/s6/ 91- s6: by Laurent Bercot, http://skarnet.org/software/s6/
92 92
93
94It is not required that a specific clone should be used. Let evolution work. 93It is not required that a specific clone should be used. Let evolution work.
95 94
95
96 Terminology 96 Terminology
97 97
98daemon: any long running background program. Common examples are sshd, getty, 98daemon: any long running background program. Common examples are sshd, getty,
diff --git a/include/bb_archive.h b/include/bb_archive.h
index 9bbf59bb8..c118fa7ec 100644
--- a/include/bb_archive.h
+++ b/include/bb_archive.h
@@ -74,6 +74,9 @@ typedef struct archive_handle_t {
74 /* Currently processed file's header */ 74 /* Currently processed file's header */
75 file_header_t *file_header; 75 file_header_t *file_header;
76 76
77 /* List of symlink placeholders */
78 llist_t *symlink_placeholders;
79
77 /* Process the header component, e.g. tar -t */ 80 /* Process the header component, e.g. tar -t */
78 void FAST_FUNC (*action_header)(const file_header_t *); 81 void FAST_FUNC (*action_header)(const file_header_t *);
79 82
@@ -198,6 +201,7 @@ char get_header_ar(archive_handle_t *archive_handle) FAST_FUNC;
198char get_header_cpio(archive_handle_t *archive_handle) FAST_FUNC; 201char get_header_cpio(archive_handle_t *archive_handle) FAST_FUNC;
199char get_header_tar(archive_handle_t *archive_handle) FAST_FUNC; 202char get_header_tar(archive_handle_t *archive_handle) FAST_FUNC;
200char get_header_tar_gz(archive_handle_t *archive_handle) FAST_FUNC; 203char get_header_tar_gz(archive_handle_t *archive_handle) FAST_FUNC;
204char get_header_tar_xz(archive_handle_t *archive_handle) FAST_FUNC;
201char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC; 205char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC;
202char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC; 206char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC;
203char get_header_tar_xz(archive_handle_t *archive_handle) FAST_FUNC; 207char get_header_tar_xz(archive_handle_t *archive_handle) FAST_FUNC;
diff --git a/include/platform.h b/include/platform.h
index 5ae82427a..9633fc725 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -54,6 +54,13 @@
54 54
55#define UNUSED_PARAM __attribute__ ((__unused__)) 55#define UNUSED_PARAM __attribute__ ((__unused__))
56#define NORETURN __attribute__ ((__noreturn__)) 56#define NORETURN __attribute__ ((__noreturn__))
57
58#if __GNUC_PREREQ(4,5)
59# define bb_unreachable(altcode) __builtin_unreachable()
60#else
61# define bb_unreachable(altcode) altcode
62#endif
63
57/* "The malloc attribute is used to tell the compiler that a function 64/* "The malloc attribute is used to tell the compiler that a function
58 * may be treated as if any non-NULL pointer it returns cannot alias 65 * may be treated as if any non-NULL pointer it returns cannot alias
59 * any other pointer valid when the function returns. This will often 66 * any other pointer valid when the function returns. This will often
@@ -416,6 +423,7 @@ typedef unsigned smalluint;
416#define HAVE_MNTENT_H 1 423#define HAVE_MNTENT_H 1
417#define HAVE_NET_ETHERNET_H 1 424#define HAVE_NET_ETHERNET_H 1
418#define HAVE_SYS_STATFS_H 1 425#define HAVE_SYS_STATFS_H 1
426#define HAVE_PRINTF_PERCENTM 1
419 427
420#if defined(__UCLIBC__) 428#if defined(__UCLIBC__)
421# if UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32) 429# if UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32)
@@ -490,6 +498,7 @@ typedef unsigned smalluint;
490# undef HAVE_DPRINTF 498# undef HAVE_DPRINTF
491# undef HAVE_UNLOCKED_STDIO 499# undef HAVE_UNLOCKED_STDIO
492# undef HAVE_UNLOCKED_LINE_OPS 500# undef HAVE_UNLOCKED_LINE_OPS
501# undef HAVE_PRINTF_PERCENTM
493#endif 502#endif
494 503
495#if defined(__dietlibc__) 504#if defined(__dietlibc__)
@@ -512,6 +521,7 @@ typedef unsigned smalluint;
512# undef HAVE_STRVERSCMP 521# undef HAVE_STRVERSCMP
513# undef HAVE_XTABS 522# undef HAVE_XTABS
514# undef HAVE_UNLOCKED_LINE_OPS 523# undef HAVE_UNLOCKED_LINE_OPS
524# undef HAVE_PRINTF_PERCENTM
515# include <osreldate.h> 525# include <osreldate.h>
516# if __FreeBSD_version < 1000029 526# if __FreeBSD_version < 1000029
517# undef HAVE_STRCHRNUL /* FreeBSD added strchrnul() between 1000028 and 1000029 */ 527# undef HAVE_STRCHRNUL /* FreeBSD added strchrnul() between 1000028 and 1000029 */
@@ -546,6 +556,7 @@ typedef unsigned smalluint;
546# undef HAVE_STRVERSCMP 556# undef HAVE_STRVERSCMP
547# undef HAVE_UNLOCKED_LINE_OPS 557# undef HAVE_UNLOCKED_LINE_OPS
548# undef HAVE_NET_ETHERNET_H 558# undef HAVE_NET_ETHERNET_H
559# undef HAVE_PRINTF_PERCENTM
549#endif 560#endif
550 561
551/* 562/*
diff --git a/libbb/Config.src b/libbb/Config.src
index 9da8b65ee..3c1b064b6 100644
--- a/libbb/Config.src
+++ b/libbb/Config.src
@@ -11,14 +11,13 @@ choice
11 prompt "Buffer allocation policy" 11 prompt "Buffer allocation policy"
12 default FEATURE_BUFFERS_USE_MALLOC 12 default FEATURE_BUFFERS_USE_MALLOC
13 help 13 help
14 There are 3 ways BusyBox can handle buffer allocations: 14 There are 3 ways busybox can handle buffer allocations:
15 - Use malloc. This costs code size for the call to xmalloc. 15 - Use malloc. This costs code size for the call to xmalloc.
16 - Put them on stack. For some very small machines with limited stack 16 - Put them on stack. For some very small machines with limited stack
17 space, this can be deadly. For most folks, this works just fine. 17 space, this can be deadly. For most folks, this works just fine.
18 - Put them in BSS. This works beautifully for computers with a real 18 - Put them in BSS. This works beautifully for computers with a real
19 MMU (and OS support), but wastes runtime RAM for uCLinux. This 19 MMU (and OS support), but wastes runtime RAM for uCLinux. This
20 behavior was the only one available for BusyBox versions 0.48 and 20 behavior was the only one available for versions 0.48 and earlier.
21 earlier.
22 21
23config FEATURE_BUFFERS_USE_MALLOC 22config FEATURE_BUFFERS_USE_MALLOC
24 bool "Allocate with Malloc" 23 bool "Allocate with Malloc"
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index 172c29611..abd9e3243 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -34,6 +34,13 @@
34# include <malloc.h> /* for mallopt */ 34# include <malloc.h> /* for mallopt */
35#endif 35#endif
36 36
37#include <sys/prctl.h>
38#ifndef PR_SET_NAME
39#define PR_SET_NAME 15
40#endif
41#ifndef PR_GET_NAME
42#define PR_GET_NAME 16
43#endif
37 44
38/* Declare <applet>_main() */ 45/* Declare <applet>_main() */
39#define PROTOTYPES 46#define PROTOTYPES
@@ -789,11 +796,26 @@ static void install_links(const char *busybox UNUSED_PARAM,
789} 796}
790# endif 797# endif
791 798
792# if ENABLE_BUSYBOX
793static void run_applet_and_exit(const char *name, char **argv) NORETURN; 799static void run_applet_and_exit(const char *name, char **argv) NORETURN;
794 800
795/* If we were called as "busybox..." */ 801# if ENABLE_BUSYBOX
796static int busybox_main(char **argv) 802# if ENABLE_FEATURE_SH_STANDALONE && ENABLE_FEATURE_TAB_COMPLETION
803 /*
804 * Insert "busybox" into applet table as well.
805 * This makes standalone shell tab-complete this name too.
806 * (Otherwise having "busybox" in applet table is not necessary,
807 * there is other code which routes "busyboxANY_SUFFIX" name
808 * to busybox_main()).
809 */
810//usage:#define busybox_trivial_usage NOUSAGE_STR
811//usage:#define busybox_full_usage ""
812//applet:IF_BUSYBOX(IF_FEATURE_SH_STANDALONE(IF_FEATURE_TAB_COMPLETION(APPLET(busybox, BB_DIR_BIN, BB_SUID_MAYBE))))
813int busybox_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE;
814# else
815# define busybox_main(argc,argv) busybox_main(argv)
816static
817# endif
818int busybox_main(int argc UNUSED_PARAM, char **argv)
797{ 819{
798 if (!argv[1]) { 820 if (!argv[1]) {
799 /* Called without arguments */ 821 /* Called without arguments */
@@ -990,7 +1012,7 @@ static NORETURN void run_applet_and_exit(const char *name, char **argv)
990{ 1012{
991# if ENABLE_BUSYBOX 1013# if ENABLE_BUSYBOX
992 if (is_prefixed_with(name, "busybox")) 1014 if (is_prefixed_with(name, "busybox"))
993 exit(busybox_main(argv)); 1015 exit(busybox_main(/*unused:*/ 0, argv));
994# endif 1016# endif
995# if NUM_APPLETS > 0 1017# if NUM_APPLETS > 0
996 /* find_applet_by_name() search is more expensive, so goes second */ 1018 /* find_applet_by_name() search is more expensive, so goes second */
@@ -1124,6 +1146,17 @@ int main(int argc UNUSED_PARAM, char **argv)
1124 } 1146 }
1125 } 1147 }
1126 applet_name = bb_basename(applet_name); 1148 applet_name = bb_basename(applet_name);
1149
1150# if defined(__linux__)
1151 /* If we are a result of execv("/proc/self/exe"), fix ugly comm of "exe" */
1152 if (ENABLE_FEATURE_SH_STANDALONE
1153 || ENABLE_FEATURE_PREFER_APPLETS
1154 || !BB_MMU
1155 ) {
1156 prctl(PR_SET_NAME, (long)applet_name, 0, 0, 0);
1157 }
1158# endif
1159
1127 parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */ 1160 parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
1128 run_applet_and_exit(applet_name, argv); 1161 run_applet_and_exit(applet_name, argv);
1129 1162
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index 670a1b194..a28b6ef98 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -834,7 +834,7 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
834 } 834 }
835 pf_len = strlen(pfind); 835 pf_len = strlen(pfind);
836 836
837#if ENABLE_FEATURE_SH_STANDALONE && NUM_APPLETS != 1 837# if ENABLE_FEATURE_SH_STANDALONE && NUM_APPLETS != 1
838 if (type == FIND_EXE_ONLY && !dirbuf) { 838 if (type == FIND_EXE_ONLY && !dirbuf) {
839 const char *p = applet_names; 839 const char *p = applet_names;
840 840
@@ -845,7 +845,7 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
845 } 845 }
846 add_partial_match(pfind, "busybox", pf_len); 846 add_partial_match(pfind, "busybox", pf_len);
847 } 847 }
848#endif 848# endif
849 849
850 for (i = 0; i < npaths; i++) { 850 for (i = 0; i < npaths; i++) {
851 DIR *dir; 851 DIR *dir;
diff --git a/libbb/u_signal_names.c b/libbb/u_signal_names.c
index bf984a44e..b82a706d8 100644
--- a/libbb/u_signal_names.c
+++ b/libbb/u_signal_names.c
@@ -143,7 +143,7 @@ int FAST_FUNC get_signum(const char *name)
143 unsigned i; 143 unsigned i;
144 144
145 i = bb_strtou(name, NULL, 10); 145 i = bb_strtou(name, NULL, 10);
146 if (!errno) 146 if (!errno && i < NSIG) /* for shells, we allow 0 too */
147 return i; 147 return i;
148 if (strncasecmp(name, "SIG", 3) == 0) 148 if (strncasecmp(name, "SIG", 3) == 0)
149 name += 3; 149 name += 3;
diff --git a/loginutils/login.c b/loginutils/login.c
index 39f703f07..381468d81 100644
--- a/loginutils/login.c
+++ b/loginutils/login.c
@@ -9,7 +9,7 @@
9//config: help 9//config: help
10//config: login is used when signing onto a system. 10//config: login is used when signing onto a system.
11//config: 11//config:
12//config: Note that Busybox binary must be setuid root for this applet to 12//config: Note that busybox binary must be setuid root for this applet to
13//config: work properly. 13//config: work properly.
14//config: 14//config:
15//config:config LOGIN_SESSION_AS_CHILD 15//config:config LOGIN_SESSION_AS_CHILD
diff --git a/loginutils/passwd.c b/loginutils/passwd.c
index 03f8ad0a4..3e1ef9abf 100644
--- a/loginutils/passwd.c
+++ b/loginutils/passwd.c
@@ -12,7 +12,7 @@
12//config: may change the password for any account. The administrator of a group 12//config: may change the password for any account. The administrator of a group
13//config: may change the password for the group. 13//config: may change the password for the group.
14//config: 14//config:
15//config: Note that Busybox binary must be setuid root for this applet to 15//config: Note that busybox binary must be setuid root for this applet to
16//config: work properly. 16//config: work properly.
17//config: 17//config:
18//config:config FEATURE_PASSWD_WEAK_CHECK 18//config:config FEATURE_PASSWD_WEAK_CHECK
diff --git a/loginutils/vlock.c b/loginutils/vlock.c
index f22abd3aa..bf46d085c 100644
--- a/loginutils/vlock.c
+++ b/loginutils/vlock.c
@@ -21,7 +21,7 @@
21//config: help 21//config: help
22//config: Build the "vlock" applet which allows you to lock (virtual) terminals. 22//config: Build the "vlock" applet which allows you to lock (virtual) terminals.
23//config: 23//config:
24//config: Note that Busybox binary must be setuid root for this applet to 24//config: Note that busybox binary must be setuid root for this applet to
25//config: work properly. 25//config: work properly.
26 26
27//applet:/* Needs to be run by root or be suid root - needs to change uid and gid: */ 27//applet:/* Needs to be run by root or be suid root - needs to change uid and gid: */
diff --git a/miscutils/crond.c b/miscutils/crond.c
index 5ae0ff084..48e429976 100644
--- a/miscutils/crond.c
+++ b/miscutils/crond.c
@@ -22,7 +22,7 @@
22//config: 40 4 * * * /etc/cron/daily > /dev/null 2>&1 22//config: 40 4 * * * /etc/cron/daily > /dev/null 2>&1
23//config: 23//config:
24//config:config FEATURE_CROND_D 24//config:config FEATURE_CROND_D
25//config: bool "Support option -d to redirect output to stderr" 25//config: bool "Support -d (redirect output to stderr)"
26//config: depends on CROND 26//config: depends on CROND
27//config: default y 27//config: default y
28//config: help 28//config: help
diff --git a/miscutils/crontab.c b/miscutils/crontab.c
index 23cb54887..804cb57f2 100644
--- a/miscutils/crontab.c
+++ b/miscutils/crontab.c
@@ -15,7 +15,7 @@
15//config: help 15//config: help
16//config: Crontab manipulates the crontab for a particular user. Only 16//config: Crontab manipulates the crontab for a particular user. Only
17//config: the superuser may specify a different user and/or crontab directory. 17//config: the superuser may specify a different user and/or crontab directory.
18//config: Note that Busybox binary must be setuid root for this applet to 18//config: Note that busybox binary must be setuid root for this applet to
19//config: work properly. 19//config: work properly.
20 20
21/* Needs to be run by root or be suid root - needs to change /var/spool/cron* files: */ 21/* Needs to be run by root or be suid root - needs to change /var/spool/cron* files: */
diff --git a/networking/ftpd.c b/networking/ftpd.c
index aee00e1c3..c562c2886 100644
--- a/networking/ftpd.c
+++ b/networking/ftpd.c
@@ -18,11 +18,12 @@
18//config: Simple FTP daemon. You have to run it via inetd. 18//config: Simple FTP daemon. You have to run it via inetd.
19//config: 19//config:
20//config:config FEATURE_FTPD_WRITE 20//config:config FEATURE_FTPD_WRITE
21//config: bool "Enable upload commands" 21//config: bool "Enable -w (upload commands)"
22//config: default y 22//config: default y
23//config: depends on FTPD 23//config: depends on FTPD
24//config: help 24//config: help
25//config: Enable all kinds of FTP upload commands (-w option) 25//config: Enable -w option. "ftpd -w" will accept upload commands
26//config: such as STOR, STOU, APPE, DELE, MKD, RMD, rename commands.
26//config: 27//config:
27//config:config FEATURE_FTPD_ACCEPT_BROKEN_LIST 28//config:config FEATURE_FTPD_ACCEPT_BROKEN_LIST
28//config: bool "Enable workaround for RFC-violating clients" 29//config: bool "Enable workaround for RFC-violating clients"
@@ -40,7 +41,13 @@
40//config: default y 41//config: default y
41//config: depends on FTPD 42//config: depends on FTPD
42//config: help 43//config: help
43//config: Enable basic system login as seen in telnet etc. 44//config: Require login, and change to logged in user's UID:GID before
45//config: accessing any files. Option "-a USER" allows "anonymous"
46//config: logins (treats them as if USER logged in).
47//config:
48//config: If this option is not selected, ftpd runs with the rights
49//config: of the user it was started under, and does not require login.
50//config: Take care to not launch it under root.
44 51
45//applet:IF_FTPD(APPLET(ftpd, BB_DIR_USR_SBIN, BB_SUID_DROP)) 52//applet:IF_FTPD(APPLET(ftpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
46 53
diff --git a/networking/httpd.c b/networking/httpd.c
index cfc07075b..079145757 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -124,7 +124,7 @@
124//config: different user. 124//config: different user.
125//config: 125//config:
126//config:config FEATURE_HTTPD_BASIC_AUTH 126//config:config FEATURE_HTTPD_BASIC_AUTH
127//config: bool "Enable Basic http Authentication" 127//config: bool "Enable HTTP authentication"
128//config: default y 128//config: default y
129//config: depends on HTTPD 129//config: depends on HTTPD
130//config: help 130//config: help
@@ -134,7 +134,7 @@
134//config: /adm:toor:PaSsWd 134//config: /adm:toor:PaSsWd
135//config: 135//config:
136//config:config FEATURE_HTTPD_AUTH_MD5 136//config:config FEATURE_HTTPD_AUTH_MD5
137//config: bool "Support MD5 crypted passwords for http Authentication" 137//config: bool "Support MD5-encrypted passwords in HTTP authentication"
138//config: default y 138//config: default y
139//config: depends on FEATURE_HTTPD_BASIC_AUTH 139//config: depends on FEATURE_HTTPD_BASIC_AUTH
140//config: help 140//config: help
diff --git a/networking/ifupdown.c b/networking/ifupdown.c
index f8c29ab00..c2cfe82ec 100644
--- a/networking/ifupdown.c
+++ b/networking/ifupdown.c
@@ -59,11 +59,11 @@
59//config: than the default of using the older "ifconfig" and "route" utilities. 59//config: than the default of using the older "ifconfig" and "route" utilities.
60//config: 60//config:
61//config: If Y: you must install either the full-blown iproute2 package 61//config: If Y: you must install either the full-blown iproute2 package
62//config: or enable "ip" applet in Busybox, or the "ifup" and "ifdown" applets 62//config: or enable "ip" applet in busybox, or the "ifup" and "ifdown" applets
63//config: will not work. 63//config: will not work.
64//config: 64//config:
65//config: If N: you must install either the full-blown ifconfig and route 65//config: If N: you must install either the full-blown ifconfig and route
66//config: utilities, or enable these applets in Busybox. 66//config: utilities, or enable these applets in busybox.
67//config: 67//config:
68//config:config FEATURE_IFUPDOWN_IPV4 68//config:config FEATURE_IFUPDOWN_IPV4
69//config: bool "Support IPv4" 69//config: bool "Support IPv4"
diff --git a/networking/inetd.c b/networking/inetd.c
index 2991edc09..91545d0a3 100644
--- a/networking/inetd.c
+++ b/networking/inetd.c
@@ -161,39 +161,57 @@
161//config: Internet superserver daemon 161//config: Internet superserver daemon
162//config: 162//config:
163//config:config FEATURE_INETD_SUPPORT_BUILTIN_ECHO 163//config:config FEATURE_INETD_SUPPORT_BUILTIN_ECHO
164//config: bool "Support echo service" 164//config: bool "Support echo service on port 7"
165//config: default y 165//config: default y
166//config: depends on INETD 166//config: depends on INETD
167//config: help 167//config: help
168//config: Echo received data internal inetd service 168//config: Internal service which echoes data back.
169//config: Activated by configuration lines like these:
170//config: echo stream tcp nowait root internal
171//config: echo dgram udp wait root internal
169//config: 172//config:
170//config:config FEATURE_INETD_SUPPORT_BUILTIN_DISCARD 173//config:config FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
171//config: bool "Support discard service" 174//config: bool "Support discard service on port 8"
172//config: default y 175//config: default y
173//config: depends on INETD 176//config: depends on INETD
174//config: help 177//config: help
175//config: Internet /dev/null internal inetd service 178//config: Internal service which discards all input.
179//config: Activated by configuration lines like these:
180//config: discard stream tcp nowait root internal
181//config: discard dgram udp wait root internal
176//config: 182//config:
177//config:config FEATURE_INETD_SUPPORT_BUILTIN_TIME 183//config:config FEATURE_INETD_SUPPORT_BUILTIN_TIME
178//config: bool "Support time service" 184//config: bool "Support time service on port 37"
179//config: default y 185//config: default y
180//config: depends on INETD 186//config: depends on INETD
181//config: help 187//config: help
182//config: Return 32 bit time since 1900 internal inetd service 188//config: Internal service which returns big-endian 32-bit number
189//config: of seconds passed since 1900-01-01. The number wraps around
190//config: on overflow.
191//config: Activated by configuration lines like these:
192//config: time stream tcp nowait root internal
193//config: time dgram udp wait root internal
183//config: 194//config:
184//config:config FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME 195//config:config FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
185//config: bool "Support daytime service" 196//config: bool "Support daytime service on port 13"
186//config: default y 197//config: default y
187//config: depends on INETD 198//config: depends on INETD
188//config: help 199//config: help
189//config: Return human-readable time internal inetd service 200//config: Internal service which returns human-readable time.
201//config: Activated by configuration lines like these:
202//config: daytime stream tcp nowait root internal
203//config: daytime dgram udp wait root internal
190//config: 204//config:
191//config:config FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN 205//config:config FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
192//config: bool "Support chargen service" 206//config: bool "Support chargen service on port 19"
193//config: default y 207//config: default y
194//config: depends on INETD 208//config: depends on INETD
195//config: help 209//config: help
196//config: Familiar character generator internal inetd service 210//config: Internal service which generates endless stream
211//config: of all ASCII chars beetween space and char 126.
212//config: Activated by configuration lines like these:
213//config: chargen stream tcp nowait root internal
214//config: chargen dgram udp wait root internal
197//config: 215//config:
198//config:config FEATURE_INETD_RPC 216//config:config FEATURE_INETD_RPC
199//config: bool "Support RPC services" 217//config: bool "Support RPC services"
@@ -216,6 +234,7 @@
216//usage: "\n -q N Socket listen queue (default 128)" 234//usage: "\n -q N Socket listen queue (default 128)"
217//usage: "\n -R N Pause services after N connects/min" 235//usage: "\n -R N Pause services after N connects/min"
218//usage: "\n (default 0 - disabled)" 236//usage: "\n (default 0 - disabled)"
237//usage: "\n Default CONFFILE is /etc/inetd.conf"
219 238
220#include <syslog.h> 239#include <syslog.h>
221#include <sys/resource.h> /* setrlimit */ 240#include <sys/resource.h> /* setrlimit */
diff --git a/networking/ip.c b/networking/ip.c
index cca7cbe12..8aaeef0db 100644
--- a/networking/ip.c
+++ b/networking/ip.c
@@ -14,8 +14,10 @@
14//config: select PLATFORM_LINUX 14//config: select PLATFORM_LINUX
15//config: help 15//config: help
16//config: The "ip" applet is a TCP/IP interface configuration and routing 16//config: The "ip" applet is a TCP/IP interface configuration and routing
17//config: utility. You generally don't need "ip" to use busybox with 17//config: utility.
18//config: TCP/IP. 18//config: Short forms (enabled below) are busybox-specific extensions.
19//config: The standard "ip" utility does not provide them. If you are
20//config: trying to be portable, it's better to use "ip CMD" forms.
19//config: 21//config:
20//config:config IPADDR 22//config:config IPADDR
21//config: bool "ipaddr (14 kb)" 23//config: bool "ipaddr (14 kb)"
@@ -23,7 +25,7 @@
23//config: select FEATURE_IP_ADDRESS 25//config: select FEATURE_IP_ADDRESS
24//config: select PLATFORM_LINUX 26//config: select PLATFORM_LINUX
25//config: help 27//config: help
26//config: Support short form of ip addr: ipaddr 28//config: Short form of "ip addr"
27//config: 29//config:
28//config:config IPLINK 30//config:config IPLINK
29//config: bool "iplink (16 kb)" 31//config: bool "iplink (16 kb)"
@@ -31,7 +33,7 @@
31//config: select FEATURE_IP_LINK 33//config: select FEATURE_IP_LINK
32//config: select PLATFORM_LINUX 34//config: select PLATFORM_LINUX
33//config: help 35//config: help
34//config: Support short form of ip link: iplink 36//config: Short form of "ip link"
35//config: 37//config:
36//config:config IPROUTE 38//config:config IPROUTE
37//config: bool "iproute (15 kb)" 39//config: bool "iproute (15 kb)"
@@ -39,7 +41,7 @@
39//config: select FEATURE_IP_ROUTE 41//config: select FEATURE_IP_ROUTE
40//config: select PLATFORM_LINUX 42//config: select PLATFORM_LINUX
41//config: help 43//config: help
42//config: Support short form of ip route: iproute 44//config: Short form of "ip route"
43//config: 45//config:
44//config:config IPTUNNEL 46//config:config IPTUNNEL
45//config: bool "iptunnel (9.6 kb)" 47//config: bool "iptunnel (9.6 kb)"
@@ -47,7 +49,7 @@
47//config: select FEATURE_IP_TUNNEL 49//config: select FEATURE_IP_TUNNEL
48//config: select PLATFORM_LINUX 50//config: select PLATFORM_LINUX
49//config: help 51//config: help
50//config: Support short form of ip tunnel: iptunnel 52//config: Short form of "ip tunnel"
51//config: 53//config:
52//config:config IPRULE 54//config:config IPRULE
53//config: bool "iprule (10 kb)" 55//config: bool "iprule (10 kb)"
@@ -55,7 +57,7 @@
55//config: select FEATURE_IP_RULE 57//config: select FEATURE_IP_RULE
56//config: select PLATFORM_LINUX 58//config: select PLATFORM_LINUX
57//config: help 59//config: help
58//config: Support short form of ip rule: iprule 60//config: Short form of "ip rule"
59//config: 61//config:
60//config:config IPNEIGH 62//config:config IPNEIGH
61//config: bool "ipneigh (8.3 kb)" 63//config: bool "ipneigh (8.3 kb)"
@@ -63,7 +65,7 @@
63//config: select FEATURE_IP_NEIGH 65//config: select FEATURE_IP_NEIGH
64//config: select PLATFORM_LINUX 66//config: select PLATFORM_LINUX
65//config: help 67//config: help
66//config: Support short form of ip neigh: ipneigh 68//config: Short form of "ip neigh"
67//config: 69//config:
68//config:config FEATURE_IP_ADDRESS 70//config:config FEATURE_IP_ADDRESS
69//config: bool "ip address" 71//config: bool "ip address"
diff --git a/networking/telnetd.c b/networking/telnetd.c
index 6e12de07a..16c572e8d 100644
--- a/networking/telnetd.c
+++ b/networking/telnetd.c
@@ -54,7 +54,7 @@
54//config: 54//config:
55//config: You need to be sure that busybox has LOGIN and 55//config: You need to be sure that busybox has LOGIN and
56//config: FEATURE_SUID enabled. And finally, you should make 56//config: FEATURE_SUID enabled. And finally, you should make
57//config: certain that Busybox has been installed setuid root: 57//config: certain that busybox has been installed setuid root:
58//config: 58//config:
59//config: chown root.root /bin/busybox 59//config: chown root.root /bin/busybox
60//config: chmod 4755 /bin/busybox 60//config: chmod 4755 /bin/busybox
diff --git a/networking/tftp.c b/networking/tftp.c
index 947e65169..5baa80448 100644
--- a/networking/tftp.c
+++ b/networking/tftp.c
@@ -22,15 +22,20 @@
22//config: bool "tftp (12 kb)" 22//config: bool "tftp (12 kb)"
23//config: default y 23//config: default y
24//config: help 24//config: help
25//config: This enables the Trivial File Transfer Protocol client program. TFTP 25//config: Trivial File Transfer Protocol client. TFTP is usually used
26//config: is usually used for simple, small transfers such as a root image 26//config: for simple, small transfers such as a root image
27//config: for a network-enabled bootloader. 27//config: for a network-enabled bootloader.
28//config: 28//config:
29//config:config FEATURE_TFTP_PROGRESS_BAR
30//config: bool "Enable progress bar"
31//config: default y
32//config: depends on TFTP
33//config:
29//config:config TFTPD 34//config:config TFTPD
30//config: bool "tftpd (10 kb)" 35//config: bool "tftpd (10 kb)"
31//config: default y 36//config: default y
32//config: help 37//config: help
33//config: This enables the Trivial File Transfer Protocol server program. 38//config: Trivial File Transfer Protocol server.
34//config: It expects that stdin is a datagram socket and a packet 39//config: It expects that stdin is a datagram socket and a packet
35//config: is already pending on it. It will exit after one transfer. 40//config: is already pending on it. It will exit after one transfer.
36//config: In other words: it should be run from inetd in nowait mode, 41//config: In other words: it should be run from inetd in nowait mode,
@@ -68,11 +73,6 @@
68//config: Allow tftp to specify block size, and tftpd to understand 73//config: Allow tftp to specify block size, and tftpd to understand
69//config: "blksize" and "tsize" options. 74//config: "blksize" and "tsize" options.
70//config: 75//config:
71//config:config FEATURE_TFTP_PROGRESS_BAR
72//config: bool "Enable progress bar"
73//config: default y
74//config: depends on TFTP && FEATURE_TFTP_BLOCKSIZE
75//config:
76//config:config TFTP_DEBUG 76//config:config TFTP_DEBUG
77//config: bool "Enable debug" 77//config: bool "Enable debug"
78//config: default n 78//config: default n
diff --git a/networking/udhcp/Config.src b/networking/udhcp/Config.src
index af2fe1835..8ab8d30ce 100644
--- a/networking/udhcp/Config.src
+++ b/networking/udhcp/Config.src
@@ -3,26 +3,14 @@
3# see scripts/kbuild/config-language.txt. 3# see scripts/kbuild/config-language.txt.
4# 4#
5 5
6INSERT
7
8config UDHCPD 6config UDHCPD
9 bool "udhcpd (DHCP server)" 7 bool "udhcpd"
10 default y 8 default y
11 select PLATFORM_LINUX 9 select PLATFORM_LINUX
12 help 10 help
13 udhcpd is a DHCP server geared primarily toward embedded systems, 11 udhcpd is a DHCP server geared primarily toward embedded systems,
14 while striving to be fully functional and RFC compliant. 12 while striving to be fully functional and RFC compliant.
15 13
16config FEATURE_UDHCPD_WRITE_LEASES_EARLY
17 bool "Rewrite the lease file at every new acknowledge"
18 default y
19 depends on UDHCPD
20 help
21 If selected, udhcpd will write a new file with leases every
22 time a new lease has been accepted, thus eliminating the need
23 to send SIGUSR1 for the initial writing or updating. Any timed
24 rewriting remains undisturbed.
25
26config FEATURE_UDHCPD_BASE_IP_ON_MAC 14config FEATURE_UDHCPD_BASE_IP_ON_MAC
27 bool "Select IP address based on client MAC" 15 bool "Select IP address based on client MAC"
28 default n 16 default n
@@ -37,6 +25,16 @@ config FEATURE_UDHCPD_BASE_IP_ON_MAC
37 for the same client to (almost always) contain the same 25 for the same client to (almost always) contain the same
38 IP address. 26 IP address.
39 27
28config FEATURE_UDHCPD_WRITE_LEASES_EARLY
29 bool "Rewrite lease file at every new acknowledge"
30 default y
31 depends on UDHCPD
32 help
33 If selected, udhcpd will write a new file with leases every
34 time a new lease has been accepted, thus eliminating the need
35 to send SIGUSR1 for the initial writing or updating. Any timed
36 rewriting remains undisturbed.
37
40config DHCPD_LEASES_FILE 38config DHCPD_LEASES_FILE
41 string "Absolute path to lease file" 39 string "Absolute path to lease file"
42 default "/var/lib/misc/udhcpd.leases" 40 default "/var/lib/misc/udhcpd.leases"
@@ -57,12 +55,12 @@ config DHCPRELAY
57 bool "dhcprelay (5.8 kb)" 55 bool "dhcprelay (5.8 kb)"
58 default y 56 default y
59 help 57 help
60 dhcprelay listens for dhcp requests on one or more interfaces 58 dhcprelay listens for DHCP requests on one or more interfaces
61 and forwards these requests to a different interface or dhcp 59 and forwards these requests to a different interface or DHCP
62 server. 60 server.
63 61
64config UDHCPC 62config UDHCPC
65 bool "udhcpc (DHCP client)" 63 bool "udhcpc"
66 default y 64 default y
67 select PLATFORM_LINUX 65 select PLATFORM_LINUX
68 help 66 help
@@ -102,19 +100,25 @@ config UDHCPC_DEFAULT_SCRIPT
102 examples/udhcp for a working example. Normally it is safe 100 examples/udhcp for a working example. Normally it is safe
103 to leave this untouched. 101 to leave this untouched.
104 102
103# udhcpc6 config is inserted here:
104INSERT
105
106comment "Common options for DHCP applets"
107 depends on UDHCPD || UDHCPC || UDHCPC6 || DHCPRELAY
108
105config FEATURE_UDHCP_PORT 109config FEATURE_UDHCP_PORT
106 bool "Enable '-P port' option for udhcpd and udhcpc" 110 bool "Enable '-P port' option for udhcpd and udhcpc"
107 default n 111 default n
108 depends on UDHCPD || UDHCPC 112 depends on UDHCPD || UDHCPC || UDHCPC6
109 help 113 help
110 At the cost of ~300 bytes, enables -P port option. 114 At the cost of ~300 bytes, enables -P port option.
111 This feature is typically not needed. 115 This feature is typically not needed.
112 116
113config UDHCP_DEBUG 117config UDHCP_DEBUG
114 int "Maximum verbosity level for udhcp applets (0..9)" 118 int "Maximum verbosity level (0..9)"
115 default 9 119 default 9
116 range 0 9 120 range 0 9
117 depends on UDHCPD || UDHCPC || DHCPRELAY 121 depends on UDHCPD || UDHCPC || UDHCPC6 || DHCPRELAY
118 help 122 help
119 Verbosity can be increased with multiple -v options. 123 Verbosity can be increased with multiple -v options.
120 This option controls how high it can be cranked up. 124 This option controls how high it can be cranked up.
@@ -122,23 +126,6 @@ config UDHCP_DEBUG
122 Bigger values result in bigger code. Levels above 1 126 Bigger values result in bigger code. Levels above 1
123 are very verbose and useful for debugging only. 127 are very verbose and useful for debugging only.
124 128
125config FEATURE_UDHCP_RFC3397
126 bool "Support RFC3397 domain search (experimental)"
127 default y
128 depends on UDHCPD || UDHCPC
129 help
130 If selected, both client and server will support passing of domain
131 search lists via option 119, specified in RFC 3397,
132 and SIP servers option 120, specified in RFC 3361.
133
134config FEATURE_UDHCP_8021Q
135 bool "Support 802.1Q VLAN parameters"
136 default y
137 depends on UDHCPD || UDHCPC
138 help
139 If selected, both client and server will support passing of VLAN
140 ID and priority via options 132 and 133 as per 802.1Q.
141
142config UDHCPC_SLACK_FOR_BUGGY_SERVERS 129config UDHCPC_SLACK_FOR_BUGGY_SERVERS
143 int "DHCP options slack buffer size" 130 int "DHCP options slack buffer size"
144 default 80 131 default 80
@@ -149,10 +136,10 @@ config UDHCPC_SLACK_FOR_BUGGY_SERVERS
149 field larger than we expect (which might also be considered a 136 field larger than we expect (which might also be considered a
150 buffer overflow attempt). These packets are normally discarded. 137 buffer overflow attempt). These packets are normally discarded.
151 If circumstances beyond your control force you to support such 138 If circumstances beyond your control force you to support such
152 servers, this may help. The upper limit (924) makes dhcpc accept 139 servers, this may help. The upper limit (924) makes udhcpc accept
153 even 1500 byte packets (maximum-sized ethernet packets). 140 even 1500 byte packets (maximum-sized ethernet packets).
154 141
155 This option does not make dhcp[cd] emit non-standard 142 This option does not make udhcp[cd] emit non-standard
156 sized packets. 143 sized packets.
157 144
158 Known buggy DHCP servers: 145 Known buggy DHCP servers:
@@ -161,3 +148,20 @@ config UDHCPC_SLACK_FOR_BUGGY_SERVERS
161 maximum size of entire IP packet, and sends packets 148 maximum size of entire IP packet, and sends packets
162 which are 28 bytes too large. 149 which are 28 bytes too large.
163 Seednet (ISP) VDSL: sends packets 2 bytes too large. 150 Seednet (ISP) VDSL: sends packets 2 bytes too large.
151
152config FEATURE_UDHCP_RFC3397
153 bool "Support RFC 3397 domain search options"
154 default y
155 depends on UDHCPD || UDHCPC
156 help
157 If selected, both client and server will support passing of domain
158 search lists via option 119, specified in RFC 3397,
159 and SIP servers option 120, specified in RFC 3361.
160
161config FEATURE_UDHCP_8021Q
162 bool "Support 802.1Q VLAN parameters options"
163 default y
164 depends on UDHCPD || UDHCPC
165 help
166 If selected, both client and server will support passing of VLAN
167 ID and priority via options 132 and 133 as per 802.1Q.
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index 5ebd05d01..43081efca 100644
--- a/networking/udhcp/d6_dhcpc.c
+++ b/networking/udhcp/d6_dhcpc.c
@@ -10,7 +10,7 @@
10 */ 10 */
11 11
12//config:config UDHCPC6 12//config:config UDHCPC6
13//config: bool "udhcpc6 (DHCPv6 client, EXPERIMENTAL)" 13//config: bool "udhcpc6"
14//config: default n # not yet ready 14//config: default n # not yet ready
15//config: depends on FEATURE_IPV6 15//config: depends on FEATURE_IPV6
16//config: help 16//config: help
diff --git a/networking/udhcp/socket.c b/networking/udhcp/socket.c
index ec6a1dc0f..34049c3ee 100644
--- a/networking/udhcp/socket.c
+++ b/networking/udhcp/socket.c
@@ -66,7 +66,7 @@ int FAST_FUNC udhcp_read_interface(const char *interface, int *ifindex, uint32_t
66 return -1; 66 return -1;
67 } 67 }
68 memcpy(mac, ifr->ifr_hwaddr.sa_data, 6); 68 memcpy(mac, ifr->ifr_hwaddr.sa_data, 6);
69 log1("MAC %02x:%02x:%02x:%02x:%02x:%02x", 69 log2("MAC %02x:%02x:%02x:%02x:%02x:%02x",
70 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 70 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
71 } 71 }
72 72
diff --git a/procps/kill.c b/procps/kill.c
index 5cff24475..09beefb2d 100644
--- a/procps/kill.c
+++ b/procps/kill.c
@@ -188,7 +188,7 @@ int kill_main(int argc UNUSED_PARAM, char **argv)
188 arg = *++argv; 188 arg = *++argv;
189 } /* else it must be -SIG */ 189 } /* else it must be -SIG */
190 signo = get_signum(arg); 190 signo = get_signum(arg);
191 if (signo < 0) { /* || signo > MAX_SIGNUM ? */ 191 if (signo < 0) {
192 bb_error_msg("bad signal name '%s'", arg); 192 bb_error_msg("bad signal name '%s'", arg);
193 return EXIT_FAILURE; 193 return EXIT_FAILURE;
194 } 194 }
diff --git a/procps/ps.c b/procps/ps.c
index c1da921bd..3e83a3e03 100644
--- a/procps/ps.c
+++ b/procps/ps.c
@@ -345,6 +345,11 @@ static void func_pgid(char *buf, int size, const procps_status_t *ps)
345 sprintf(buf, "%*u", size, ps->pgid); 345 sprintf(buf, "%*u", size, ps->pgid);
346} 346}
347 347
348static void func_sid(char *buf, int size, const procps_status_t *ps)
349{
350 sprintf(buf, "%*u", size, ps->sid);
351}
352
348static void put_lu(char *buf, int size, unsigned long u) 353static void put_lu(char *buf, int size, unsigned long u)
349{ 354{
350 char buf4[5]; 355 char buf4[5];
@@ -430,11 +435,6 @@ static void func_label(char *buf, int size, const procps_status_t *ps)
430#endif 435#endif
431 436
432/* 437/*
433static void func_nice(char *buf, int size, const procps_status_t *ps)
434{
435 ps->???
436}
437
438static void func_pcpu(char *buf, int size, const procps_status_t *ps) 438static void func_pcpu(char *buf, int size, const procps_status_t *ps)
439{ 439{
440} 440}
@@ -471,6 +471,7 @@ static const ps_out_t out_spec[] = {
471 { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY }, 471 { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY },
472 { 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ }, 472 { 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ },
473/* Not mandated, but useful: */ 473/* Not mandated, but useful: */
474 { 5 , "sid" ,"SID" ,func_sid ,PSSCAN_SID },
474 { 4 , "stat" ,"STAT" ,func_state ,PSSCAN_STATE }, 475 { 4 , "stat" ,"STAT" ,func_state ,PSSCAN_STATE },
475 { 4 , "rss" ,"RSS" ,func_rss ,PSSCAN_RSS }, 476 { 4 , "rss" ,"RSS" ,func_rss ,PSSCAN_RSS },
476#endif 477#endif
diff --git a/shell/ash.c b/shell/ash.c
index 28b522d7c..6aaeecfac 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -241,6 +241,9 @@
241#include "shell_common.h" 241#include "shell_common.h"
242#if ENABLE_FEATURE_SH_MATH 242#if ENABLE_FEATURE_SH_MATH
243# include "math.h" 243# include "math.h"
244#else
245typedef long arith_t;
246# define ARITH_FMT "%ld"
244#endif 247#endif
245#if ENABLE_ASH_RANDOM_SUPPORT 248#if ENABLE_ASH_RANDOM_SUPPORT
246# include "random.h" 249# include "random.h"
@@ -708,8 +711,8 @@ fmtstr(char *outbuf, size_t length, const char *fmt, ...)
708 va_list ap; 711 va_list ap;
709 int ret; 712 int ret;
710 713
711 va_start(ap, fmt);
712 INT_OFF; 714 INT_OFF;
715 va_start(ap, fmt);
713 ret = vsnprintf(outbuf, length, fmt, ap); 716 ret = vsnprintf(outbuf, length, fmt, ap);
714 va_end(ap); 717 va_end(ap);
715 INT_ON; 718 INT_ON;
@@ -1334,7 +1337,6 @@ static struct parsefile basepf; /* top level input file */
1334static struct parsefile *g_parsefile = &basepf; /* current input file */ 1337static struct parsefile *g_parsefile = &basepf; /* current input file */
1335static int startlinno; /* line # where last token started */ 1338static int startlinno; /* line # where last token started */
1336static char *commandname; /* currently executing command */ 1339static char *commandname; /* currently executing command */
1337static struct strlist *cmdenviron; /* environment for builtin command */
1338 1340
1339 1341
1340/* ============ Message printing */ 1342/* ============ Message printing */
@@ -1391,6 +1393,18 @@ ash_msg_and_raise_error(const char *msg, ...)
1391 va_end(ap); 1393 va_end(ap);
1392} 1394}
1393 1395
1396/*
1397 * Use '%m' to append error string on platforms that support it, '%s' and
1398 * strerror() on those that don't.
1399 *
1400 * 'fmt' must be a string literal.
1401 */
1402#ifdef HAVE_PRINTF_PERCENTM
1403#define ash_msg_and_raise_perror(fmt, ...) ash_msg_and_raise_error(fmt ": %m", ##__VA_ARGS__)
1404#else
1405#define ash_msg_and_raise_perror(fmt, ...) ash_msg_and_raise_error(fmt ": %s", ##__VA_ARGS__, strerror(errno))
1406#endif
1407
1394static void raise_error_syntax(const char *) NORETURN; 1408static void raise_error_syntax(const char *) NORETURN;
1395static void 1409static void
1396raise_error_syntax(const char *msg) 1410raise_error_syntax(const char *msg)
@@ -1875,9 +1889,6 @@ single_quote(const char *s)
1875 1889
1876/* 1890/*
1877 * Produce a possibly single quoted string suitable as input to the shell. 1891 * Produce a possibly single quoted string suitable as input to the shell.
1878 * If 'conditional' is nonzero, quoting is only done if the string contains
1879 * non-shellsafe characters, or is identical to a shell keyword (reserved
1880 * word); if it is zero, quoting is always done.
1881 * If quoting was done, the return string is allocated on the stack, 1892 * If quoting was done, the return string is allocated on the stack,
1882 * otherwise a pointer to the original string is returned. 1893 * otherwise a pointer to the original string is returned.
1883 */ 1894 */
@@ -2312,15 +2323,9 @@ reinit_unicode_for_ash(void)
2312/* 2323/*
2313 * Search the environment of a builtin command. 2324 * Search the environment of a builtin command.
2314 */ 2325 */
2315static const char * 2326static ALWAYS_INLINE const char *
2316bltinlookup(const char *name) 2327bltinlookup(const char *name)
2317{ 2328{
2318 struct strlist *sp;
2319
2320 for (sp = cmdenviron; sp; sp = sp->next) {
2321 if (varcmp(sp->text, name) == 0)
2322 return var_end(sp->text);
2323 }
2324 return lookupvar(name); 2329 return lookupvar(name);
2325} 2330}
2326 2331
@@ -2331,14 +2336,15 @@ bltinlookup(const char *name)
2331 * will go away. 2336 * will go away.
2332 * Called with interrupts off. 2337 * Called with interrupts off.
2333 */ 2338 */
2334static void 2339static struct var *
2335setvareq(char *s, int flags) 2340setvareq(char *s, int flags)
2336{ 2341{
2337 struct var *vp, **vpp; 2342 struct var *vp, **vpp;
2338 2343
2339 vpp = hashvar(s); 2344 vpp = hashvar(s);
2340 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); 2345 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2341 vp = *findvar(vpp, s); 2346 vpp = findvar(vpp, s);
2347 vp = *vpp;
2342 if (vp) { 2348 if (vp) {
2343 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) { 2349 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2344 const char *n; 2350 const char *n;
@@ -2351,7 +2357,7 @@ setvareq(char *s, int flags)
2351 } 2357 }
2352 2358
2353 if (flags & VNOSET) 2359 if (flags & VNOSET)
2354 return; 2360 goto out;
2355 2361
2356 if (vp->var_func && !(flags & VNOFUNC)) 2362 if (vp->var_func && !(flags & VNOFUNC))
2357 vp->var_func(var_end(s)); 2363 vp->var_func(var_end(s));
@@ -2359,11 +2365,22 @@ setvareq(char *s, int flags)
2359 if (!(vp->flags & (VTEXTFIXED|VSTACK))) 2365 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2360 free((char*)vp->var_text); 2366 free((char*)vp->var_text);
2361 2367
2368 if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) {
2369 *vpp = vp->next;
2370 free(vp);
2371 out_free:
2372 if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
2373 free(s);
2374 goto out;
2375 }
2376
2362 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET); 2377 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2363 } else { 2378 } else {
2364 /* variable s is not found */ 2379 /* variable s is not found */
2365 if (flags & VNOSET) 2380 if (flags & VNOSET)
2366 return; 2381 goto out;
2382 if ((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET)
2383 goto out_free;
2367 vp = ckzalloc(sizeof(*vp)); 2384 vp = ckzalloc(sizeof(*vp));
2368 vp->next = *vpp; 2385 vp->next = *vpp;
2369 /*vp->func = NULL; - ckzalloc did it */ 2386 /*vp->func = NULL; - ckzalloc did it */
@@ -2373,13 +2390,16 @@ setvareq(char *s, int flags)
2373 s = ckstrdup(s); 2390 s = ckstrdup(s);
2374 vp->var_text = s; 2391 vp->var_text = s;
2375 vp->flags = flags; 2392 vp->flags = flags;
2393
2394 out:
2395 return vp;
2376} 2396}
2377 2397
2378/* 2398/*
2379 * Set the value of a variable. The flags argument is ored with the 2399 * Set the value of a variable. The flags argument is ored with the
2380 * flags of the variable. If val is NULL, the variable is unset. 2400 * flags of the variable. If val is NULL, the variable is unset.
2381 */ 2401 */
2382static void 2402static struct var *
2383setvar(const char *name, const char *val, int flags) 2403setvar(const char *name, const char *val, int flags)
2384{ 2404{
2385 const char *q; 2405 const char *q;
@@ -2387,6 +2407,7 @@ setvar(const char *name, const char *val, int flags)
2387 char *nameeq; 2407 char *nameeq;
2388 size_t namelen; 2408 size_t namelen;
2389 size_t vallen; 2409 size_t vallen;
2410 struct var *vp;
2390 2411
2391 q = endofname(name); 2412 q = endofname(name);
2392 p = strchrnul(q, '='); 2413 p = strchrnul(q, '=');
@@ -2408,8 +2429,10 @@ setvar(const char *name, const char *val, int flags)
2408 p = mempcpy(p, val, vallen); 2429 p = mempcpy(p, val, vallen);
2409 } 2430 }
2410 *p = '\0'; 2431 *p = '\0';
2411 setvareq(nameeq, flags | VNOSAVE); 2432 vp = setvareq(nameeq, flags | VNOSAVE);
2412 INT_ON; 2433 INT_ON;
2434
2435 return vp;
2413} 2436}
2414 2437
2415static void FAST_FUNC 2438static void FAST_FUNC
@@ -2421,43 +2444,10 @@ setvar0(const char *name, const char *val)
2421/* 2444/*
2422 * Unset the specified variable. 2445 * Unset the specified variable.
2423 */ 2446 */
2424static int 2447static void
2425unsetvar(const char *s) 2448unsetvar(const char *s)
2426{ 2449{
2427 struct var **vpp; 2450 setvar(s, NULL, 0);
2428 struct var *vp;
2429 int retval;
2430
2431 vpp = findvar(hashvar(s), s);
2432 vp = *vpp;
2433 retval = 2;
2434 if (vp) {
2435 int flags = vp->flags;
2436
2437 retval = 1;
2438 if (flags & VREADONLY)
2439 goto out;
2440#if ENABLE_ASH_RANDOM_SUPPORT
2441 vp->flags &= ~VDYNAMIC;
2442#endif
2443 if (flags & VUNSET)
2444 goto ok;
2445 if ((flags & VSTRFIXED) == 0) {
2446 INT_OFF;
2447 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2448 free((char*)vp->var_text);
2449 *vpp = vp->next;
2450 free(vp);
2451 INT_ON;
2452 } else {
2453 setvar0(s, NULL);
2454 vp->flags &= ~VEXPORT;
2455 }
2456 ok:
2457 retval = 0;
2458 }
2459 out:
2460 return retval;
2461} 2451}
2462 2452
2463/* 2453/*
@@ -4067,7 +4057,7 @@ static void
4067xtcsetpgrp(int fd, pid_t pgrp) 4057xtcsetpgrp(int fd, pid_t pgrp)
4068{ 4058{
4069 if (tcsetpgrp(fd, pgrp)) 4059 if (tcsetpgrp(fd, pgrp))
4070 ash_msg_and_raise_error("can't set tty process group (%m)"); 4060 ash_msg_and_raise_perror("can't set tty process group");
4071} 4061}
4072 4062
4073/* 4063/*
@@ -5529,68 +5519,6 @@ stoppedjobs(void)
5529#define CLOSED -3 /* marks a slot of previously-closed fd */ 5519#define CLOSED -3 /* marks a slot of previously-closed fd */
5530 5520
5531/* 5521/*
5532 * Open a file in noclobber mode.
5533 * The code was copied from bash.
5534 */
5535static int
5536noclobberopen(const char *fname)
5537{
5538 int r, fd;
5539 struct stat finfo, finfo2;
5540
5541 /*
5542 * If the file exists and is a regular file, return an error
5543 * immediately.
5544 */
5545 r = stat(fname, &finfo);
5546 if (r == 0 && S_ISREG(finfo.st_mode)) {
5547 errno = EEXIST;
5548 return -1;
5549 }
5550
5551 /*
5552 * If the file was not present (r != 0), make sure we open it
5553 * exclusively so that if it is created before we open it, our open
5554 * will fail. Make sure that we do not truncate an existing file.
5555 * Note that we don't turn on O_EXCL unless the stat failed -- if the
5556 * file was not a regular file, we leave O_EXCL off.
5557 */
5558 if (r != 0)
5559 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
5560 fd = open(fname, O_WRONLY|O_CREAT, 0666);
5561
5562 /* If the open failed, return the file descriptor right away. */
5563 if (fd < 0)
5564 return fd;
5565
5566 /*
5567 * OK, the open succeeded, but the file may have been changed from a
5568 * non-regular file to a regular file between the stat and the open.
5569 * We are assuming that the O_EXCL open handles the case where FILENAME
5570 * did not exist and is symlinked to an existing file between the stat
5571 * and open.
5572 */
5573
5574 /*
5575 * If we can open it and fstat the file descriptor, and neither check
5576 * revealed that it was a regular file, and the file has not been
5577 * replaced, return the file descriptor.
5578 */
5579 if (fstat(fd, &finfo2) == 0
5580 && !S_ISREG(finfo2.st_mode)
5581 && finfo.st_dev == finfo2.st_dev
5582 && finfo.st_ino == finfo2.st_ino
5583 ) {
5584 return fd;
5585 }
5586
5587 /* The file has been replaced. badness. */
5588 close(fd);
5589 errno = EEXIST;
5590 return -1;
5591}
5592
5593/*
5594 * Handle here documents. Normally we fork off a process to write the 5522 * Handle here documents. Normally we fork off a process to write the
5595 * data to a pipe. If the document is short, we can stuff the data in 5523 * data to a pipe. If the document is short, we can stuff the data in
5596 * the pipe without forking. 5524 * the pipe without forking.
@@ -5645,6 +5573,7 @@ openhere(union node *redir)
5645static int 5573static int
5646openredirect(union node *redir) 5574openredirect(union node *redir)
5647{ 5575{
5576 struct stat sb;
5648 char *fname; 5577 char *fname;
5649 int f; 5578 int f;
5650 5579
@@ -5709,9 +5638,23 @@ openredirect(union node *redir)
5709#endif 5638#endif
5710 /* Take care of noclobber mode. */ 5639 /* Take care of noclobber mode. */
5711 if (Cflag) { 5640 if (Cflag) {
5712 f = noclobberopen(fname); 5641 if (stat(fname, &sb) < 0) {
5713 if (f < 0) 5642 f = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
5643 if (f < 0)
5644 goto ecreate;
5645 } else if (!S_ISREG(sb.st_mode)) {
5646 f = open(fname, O_WRONLY, 0666);
5647 if (f < 0)
5648 goto ecreate;
5649 if (fstat(f, &sb) < 0 && S_ISREG(sb.st_mode)) {
5650 close(f);
5651 errno = EEXIST;
5652 goto ecreate;
5653 }
5654 } else {
5655 errno = EEXIST;
5714 goto ecreate; 5656 goto ecreate;
5657 }
5715 break; 5658 break;
5716 } 5659 }
5717 /* FALLTHROUGH */ 5660 /* FALLTHROUGH */
@@ -5750,7 +5693,7 @@ savefd(int from)
5750 err = newfd < 0 ? errno : 0; 5693 err = newfd < 0 ? errno : 0;
5751 if (err != EBADF) { 5694 if (err != EBADF) {
5752 if (err) 5695 if (err)
5753 ash_msg_and_raise_error("%d: %m", from); 5696 ash_msg_and_raise_perror("%d", from);
5754 close(from); 5697 close(from);
5755 fcntl(newfd, F_SETFD, FD_CLOEXEC); 5698 fcntl(newfd, F_SETFD, FD_CLOEXEC);
5756 } 5699 }
@@ -5765,7 +5708,7 @@ dup2_or_raise(int from, int to)
5765 newfd = (from != to) ? dup2(from, to) : to; 5708 newfd = (from != to) ? dup2(from, to) : to;
5766 if (newfd < 0) { 5709 if (newfd < 0) {
5767 /* Happens when source fd is not open: try "echo >&99" */ 5710 /* Happens when source fd is not open: try "echo >&99" */
5768 ash_msg_and_raise_error("%d: %m", from); 5711 ash_msg_and_raise_perror("%d", from);
5769 } 5712 }
5770 return newfd; 5713 return newfd;
5771} 5714}
@@ -5896,7 +5839,7 @@ redirect(union node *redir, int flags)
5896 /* "echo >&10" and 10 is a fd opened to a sh script? */ 5839 /* "echo >&10" and 10 is a fd opened to a sh script? */
5897 if (is_hidden_fd(sv, right_fd)) { 5840 if (is_hidden_fd(sv, right_fd)) {
5898 errno = EBADF; /* as if it is closed */ 5841 errno = EBADF; /* as if it is closed */
5899 ash_msg_and_raise_error("%d: %m", right_fd); 5842 ash_msg_and_raise_perror("%d", right_fd);
5900 } 5843 }
5901 newfd = -1; 5844 newfd = -1;
5902 } else { 5845 } else {
@@ -5930,7 +5873,7 @@ redirect(union node *redir, int flags)
5930 if (newfd >= 0) 5873 if (newfd >= 0)
5931 close(newfd); 5874 close(newfd);
5932 errno = i; 5875 errno = i;
5933 ash_msg_and_raise_error("%d: %m", fd); 5876 ash_msg_and_raise_perror("%d", fd);
5934 /* NOTREACHED */ 5877 /* NOTREACHED */
5935 } 5878 }
5936 /* EBADF: it is not open - good, remember to close it */ 5879 /* EBADF: it is not open - good, remember to close it */
@@ -6104,7 +6047,7 @@ ash_arith(const char *s)
6104#define RMESCAPE_SLASH 0x20 /* Stop globbing after slash */ 6047#define RMESCAPE_SLASH 0x20 /* Stop globbing after slash */
6105 6048
6106/* Add CTLESC when necessary. */ 6049/* Add CTLESC when necessary. */
6107#define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT | EXP_REDIR) 6050#define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT)
6108/* Do not skip NUL characters. */ 6051/* Do not skip NUL characters. */
6109#define QUOTES_KEEPNUL EXP_TILDE 6052#define QUOTES_KEEPNUL EXP_TILDE
6110 6053
@@ -6137,19 +6080,20 @@ static struct arglist exparg;
6137 6080
6138/* 6081/*
6139 * Our own itoa(). 6082 * Our own itoa().
6083 * cvtnum() is used even if math support is off (to prepare $? values and such).
6140 */ 6084 */
6141#if !ENABLE_FEATURE_SH_MATH
6142/* cvtnum() is used even if math support is off (to prepare $? values and such) */
6143typedef long arith_t;
6144# define ARITH_FMT "%ld"
6145#endif
6146static int 6085static int
6147cvtnum(arith_t num) 6086cvtnum(arith_t num)
6148{ 6087{
6149 int len; 6088 int len;
6150 6089
6151 expdest = makestrspace(sizeof(arith_t)*3 + 2, expdest); 6090 /* 32-bit and wider ints require buffer size of bytes*3 (or less) */
6152 len = fmtstr(expdest, sizeof(arith_t)*3 + 2, ARITH_FMT, num); 6091 len = sizeof(arith_t) * 3;
6092 /* If narrower: worst case, 1-byte ints: need 5 bytes: "-127<NUL>" */
6093 if (sizeof(arith_t) < 4) len += 2;
6094
6095 expdest = makestrspace(len, expdest);
6096 len = fmtstr(expdest, len, ARITH_FMT, num);
6153 STADJUST(len, expdest); 6097 STADJUST(len, expdest);
6154 return len; 6098 return len;
6155} 6099}
@@ -6569,9 +6513,24 @@ struct backcmd { /* result of evalbackcmd */
6569}; 6513};
6570 6514
6571/* These forward decls are needed to use "eval" code for backticks handling: */ 6515/* These forward decls are needed to use "eval" code for backticks handling: */
6572#define EV_EXIT 01 /* exit after evaluating tree */ 6516/* flags in argument to evaltree */
6517#define EV_EXIT 01 /* exit after evaluating tree */
6518#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
6573static int evaltree(union node *, int); 6519static int evaltree(union node *, int);
6574 6520
6521/* An evaltree() which is known to never return.
6522 * Used to use an alias:
6523 * static int evaltreenr(union node *, int) __attribute__((alias("evaltree"),__noreturn__));
6524 * but clang was reported to "transfer" noreturn-ness to evaltree() as well.
6525 */
6526static ALWAYS_INLINE NORETURN void
6527evaltreenr(union node *n, int flags)
6528{
6529 evaltree(n, flags);
6530 bb_unreachable(abort());
6531 /* NOTREACHED */
6532}
6533
6575static void FAST_FUNC 6534static void FAST_FUNC
6576evalbackcmd(union node *n, struct backcmd *result) 6535evalbackcmd(union node *n, struct backcmd *result)
6577{ 6536{
@@ -6617,7 +6576,7 @@ evalbackcmd(union node *n, struct backcmd *result)
6617 */ 6576 */
6618 eflag = 0; 6577 eflag = 0;
6619 ifsfree(); 6578 ifsfree();
6620 evaltree(n, EV_EXIT); /* actually evaltreenr... */ 6579 evaltreenr(n, EV_EXIT);
6621 /* NOTREACHED */ 6580 /* NOTREACHED */
6622 } 6581 }
6623#endif 6582#endif
@@ -6750,19 +6709,15 @@ expari(int flag)
6750#endif 6709#endif
6751 6710
6752/* argstr needs it */ 6711/* argstr needs it */
6753static char *evalvar(char *p, int flags, struct strlist *var_str_list); 6712static char *evalvar(char *p, int flags);
6754 6713
6755/* 6714/*
6756 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC 6715 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
6757 * characters to allow for further processing. Otherwise treat 6716 * characters to allow for further processing. Otherwise treat
6758 * $@ like $* since no splitting will be performed. 6717 * $@ like $* since no splitting will be performed.
6759 *
6760 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
6761 * over shell variables. Needed for "A=a B=$A; echo $B" case - we use it
6762 * for correct expansion of "B=$A" word.
6763 */ 6718 */
6764static void 6719static void
6765argstr(char *p, int flags, struct strlist *var_str_list) 6720argstr(char *p, int flags)
6766{ 6721{
6767 static const char spclchars[] ALIGN1 = { 6722 static const char spclchars[] ALIGN1 = {
6768 '=', 6723 '=',
@@ -6855,7 +6810,7 @@ argstr(char *p, int flags, struct strlist *var_str_list)
6855 inquotes ^= EXP_QUOTED; 6810 inquotes ^= EXP_QUOTED;
6856 /* "$@" syntax adherence hack */ 6811 /* "$@" syntax adherence hack */
6857 if (inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) { 6812 if (inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) {
6858 p = evalvar(p + 1, flags | inquotes, /* var_str_list: */ NULL) + 1; 6813 p = evalvar(p + 1, flags | inquotes) + 1;
6859 goto start; 6814 goto start;
6860 } 6815 }
6861 addquote: 6816 addquote:
@@ -6881,7 +6836,7 @@ argstr(char *p, int flags, struct strlist *var_str_list)
6881 goto addquote; 6836 goto addquote;
6882 case CTLVAR: 6837 case CTLVAR:
6883 TRACE(("argstr: evalvar('%s')\n", p)); 6838 TRACE(("argstr: evalvar('%s')\n", p));
6884 p = evalvar(p, flags | inquotes, var_str_list); 6839 p = evalvar(p, flags | inquotes);
6885 TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock())); 6840 TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock()));
6886 goto start; 6841 goto start;
6887 case CTLBACKQ: 6842 case CTLBACKQ:
@@ -7023,7 +6978,7 @@ varunset(const char *end, const char *var, const char *umsg, int varflags)
7023 6978
7024static const char * 6979static const char *
7025subevalvar(char *p, char *varname, int strloc, int subtype, 6980subevalvar(char *p, char *varname, int strloc, int subtype,
7026 int startloc, int varflags, int flag, struct strlist *var_str_list) 6981 int startloc, int varflags, int flag)
7027{ 6982{
7028 struct nodelist *saveargbackq = argbackq; 6983 struct nodelist *saveargbackq = argbackq;
7029 int quotes = flag & QUOTES_ESC; 6984 int quotes = flag & QUOTES_ESC;
@@ -7041,8 +6996,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
7041 // p, varname, strloc, subtype, startloc, varflags, quotes); 6996 // p, varname, strloc, subtype, startloc, varflags, quotes);
7042 6997
7043 argstr(p, EXP_TILDE | (subtype != VSASSIGN && subtype != VSQUESTION ? 6998 argstr(p, EXP_TILDE | (subtype != VSASSIGN && subtype != VSQUESTION ?
7044 (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE) : 0), 6999 (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE) : 0)
7045 var_str_list); 7000 );
7046 STPUTC('\0', expdest); 7001 STPUTC('\0', expdest);
7047 argbackq = saveargbackq; 7002 argbackq = saveargbackq;
7048 startp = (char *)stackblock() + startloc; 7003 startp = (char *)stackblock() + startloc;
@@ -7319,7 +7274,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
7319 * ash -c 'echo ${#1#}' name:'1=#' 7274 * ash -c 'echo ${#1#}' name:'1=#'
7320 */ 7275 */
7321static NOINLINE ssize_t 7276static NOINLINE ssize_t
7322varvalue(char *name, int varflags, int flags, struct strlist *var_str_list, int *quotedp) 7277varvalue(char *name, int varflags, int flags, int *quotedp)
7323{ 7278{
7324 const char *p; 7279 const char *p;
7325 int num; 7280 int num;
@@ -7411,31 +7366,6 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list, int
7411 goto value; 7366 goto value;
7412 default: 7367 default:
7413 /* NB: name has form "VAR=..." */ 7368 /* NB: name has form "VAR=..." */
7414
7415 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
7416 * which should be considered before we check variables. */
7417 if (var_str_list) {
7418 unsigned name_len = (strchrnul(name, '=') - name) + 1;
7419 p = NULL;
7420 do {
7421 char *str, *eq;
7422 str = var_str_list->text;
7423 eq = strchr(str, '=');
7424 if (!eq) /* stop at first non-assignment */
7425 break;
7426 eq++;
7427 if (name_len == (unsigned)(eq - str)
7428 && strncmp(str, name, name_len) == 0
7429 ) {
7430 p = eq;
7431 /* goto value; - WRONG! */
7432 /* think "A=1 A=2 B=$A" */
7433 }
7434 var_str_list = var_str_list->next;
7435 } while (var_str_list);
7436 if (p)
7437 goto value;
7438 }
7439 p = lookupvar(name); 7369 p = lookupvar(name);
7440 value: 7370 value:
7441 if (!p) 7371 if (!p)
@@ -7465,7 +7395,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list, int
7465 * input string. 7395 * input string.
7466 */ 7396 */
7467static char * 7397static char *
7468evalvar(char *p, int flag, struct strlist *var_str_list) 7398evalvar(char *p, int flag)
7469{ 7399{
7470 char varflags; 7400 char varflags;
7471 char subtype; 7401 char subtype;
@@ -7489,7 +7419,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list)
7489 p = strchr(p, '=') + 1; //TODO: use var_end(p)? 7419 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
7490 7420
7491 again: 7421 again:
7492 varlen = varvalue(var, varflags, flag, var_str_list, &quoted); 7422 varlen = varvalue(var, varflags, flag, &quoted);
7493 if (varflags & VSNUL) 7423 if (varflags & VSNUL)
7494 varlen--; 7424 varlen--;
7495 7425
@@ -7503,8 +7433,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list)
7503 if (varlen < 0) { 7433 if (varlen < 0) {
7504 argstr( 7434 argstr(
7505 p, 7435 p,
7506 flag | EXP_TILDE | EXP_WORD, 7436 flag | EXP_TILDE | EXP_WORD
7507 var_str_list
7508 ); 7437 );
7509 goto end; 7438 goto end;
7510 } 7439 }
@@ -7516,7 +7445,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list)
7516 goto record; 7445 goto record;
7517 7446
7518 subevalvar(p, var, 0, subtype, startloc, varflags, 7447 subevalvar(p, var, 0, subtype, startloc, varflags,
7519 flag & ~QUOTES_ESC, var_str_list); 7448 flag & ~QUOTES_ESC);
7520 varflags &= ~VSNUL; 7449 varflags &= ~VSNUL;
7521 /* 7450 /*
7522 * Remove any recorded regions beyond 7451 * Remove any recorded regions beyond
@@ -7569,7 +7498,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list)
7569 STPUTC('\0', expdest); 7498 STPUTC('\0', expdest);
7570 patloc = expdest - (char *)stackblock(); 7499 patloc = expdest - (char *)stackblock();
7571 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype, 7500 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
7572 startloc, varflags, flag, var_str_list)) { 7501 startloc, varflags, flag)) {
7573 int amount = expdest - ( 7502 int amount = expdest - (
7574 (char *)stackblock() + patloc - 1 7503 (char *)stackblock() + patloc - 1
7575 ); 7504 );
@@ -7993,8 +7922,7 @@ expandarg(union node *arg, struct arglist *arglist, int flag)
7993 argbackq = arg->narg.backquote; 7922 argbackq = arg->narg.backquote;
7994 STARTSTACKSTR(expdest); 7923 STARTSTACKSTR(expdest);
7995 TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag)); 7924 TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag));
7996 argstr(arg->narg.text, flag, 7925 argstr(arg->narg.text, flag);
7997 /* var_str_list: */ arglist ? arglist->list : NULL);
7998 p = _STPUTC('\0', expdest); 7926 p = _STPUTC('\0', expdest);
7999 expdest = p - 1; 7927 expdest = p - 1;
8000 if (arglist == NULL) { 7928 if (arglist == NULL) {
@@ -8013,10 +7941,6 @@ expandarg(union node *arg, struct arglist *arglist, int flag)
8013 exparg.lastp = &exparg.list; 7941 exparg.lastp = &exparg.list;
8014 expandmeta(exparg.list /*, flag*/); 7942 expandmeta(exparg.list /*, flag*/);
8015 } else { 7943 } else {
8016 if (flag & EXP_REDIR) { /*XXX - for now, just remove escapes */
8017 rmescapes(p, 0);
8018 TRACE(("expandarg: rmescapes:'%s'\n", p));
8019 }
8020 sp = stzalloc(sizeof(*sp)); 7944 sp = stzalloc(sizeof(*sp));
8021 sp->text = p; 7945 sp->text = p;
8022 *exparg.lastp = sp; 7946 *exparg.lastp = sp;
@@ -8065,8 +7989,7 @@ casematch(union node *pattern, char *val)
8065 setstackmark(&smark); 7989 setstackmark(&smark);
8066 argbackq = pattern->narg.backquote; 7990 argbackq = pattern->narg.backquote;
8067 STARTSTACKSTR(expdest); 7991 STARTSTACKSTR(expdest);
8068 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE, 7992 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
8069 /* var_str_list: */ NULL);
8070 STACKSTRNUL(expdest); 7993 STACKSTRNUL(expdest);
8071 ifsfree(); 7994 ifsfree();
8072 result = patmatch(stackblock(), val); 7995 result = patmatch(stackblock(), val);
@@ -8144,7 +8067,7 @@ static int builtinloc = -1; /* index in path of %builtin, or -1 */
8144 8067
8145 8068
8146static void 8069static void
8147tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp) 8070tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp)
8148{ 8071{
8149#if ENABLE_FEATURE_SH_STANDALONE 8072#if ENABLE_FEATURE_SH_STANDALONE
8150 if (applet_no >= 0) { 8073 if (applet_no >= 0) {
@@ -8152,6 +8075,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **
8152 clearenv(); 8075 clearenv();
8153 while (*envp) 8076 while (*envp)
8154 putenv(*envp++); 8077 putenv(*envp++);
8078 popredir(/*drop:*/ 1, /*restore:*/ 0);
8155 run_applet_no_and_exit(applet_no, cmd, argv); 8079 run_applet_no_and_exit(applet_no, cmd, argv);
8156 } 8080 }
8157 /* re-exec ourselves with the new arguments */ 8081 /* re-exec ourselves with the new arguments */
@@ -8169,7 +8093,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **
8169#else 8093#else
8170 execve(cmd, argv, envp); 8094 execve(cmd, argv, envp);
8171#endif 8095#endif
8172 if (cmd != (char*) bb_busybox_exec_path && errno == ENOEXEC) { 8096 if (cmd != bb_busybox_exec_path && errno == ENOEXEC) {
8173 /* Run "cmd" as a shell script: 8097 /* Run "cmd" as a shell script:
8174 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html 8098 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
8175 * "If the execve() function fails with ENOEXEC, the shell 8099 * "If the execve() function fails with ENOEXEC, the shell
@@ -8186,8 +8110,8 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **
8186 * message and exit code 126. For one, this prevents attempts 8110 * message and exit code 126. For one, this prevents attempts
8187 * to interpret foreign ELF binaries as shell scripts. 8111 * to interpret foreign ELF binaries as shell scripts.
8188 */ 8112 */
8189 argv[0] = cmd; 8113 argv[0] = (char*) cmd;
8190 cmd = (char*) bb_busybox_exec_path; 8114 cmd = bb_busybox_exec_path;
8191 /* NB: this is only possible because all callers of shellexec() 8115 /* NB: this is only possible because all callers of shellexec()
8192 * ensure that the argv[-1] slot exists! 8116 * ensure that the argv[-1] slot exists!
8193 */ 8117 */
@@ -8810,10 +8734,6 @@ static int nodeptrsize;
8810static char **nodeptr; 8734static char **nodeptr;
8811#endif 8735#endif
8812 8736
8813/* flags in argument to evaltree */
8814#define EV_EXIT 01 /* exit after evaluating tree */
8815#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
8816
8817static const uint8_t nodesize[N_NUMBER] ALIGN1 = { 8737static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
8818 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), 8738 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
8819 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)), 8739 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
@@ -9336,11 +9256,6 @@ evaltree(union node *n, int flags)
9336 return exitstatus; 9256 return exitstatus;
9337} 9257}
9338 9258
9339#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
9340static
9341#endif
9342int evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
9343
9344static int 9259static int
9345skiploop(void) 9260skiploop(void)
9346{ 9261{
@@ -9699,27 +9614,57 @@ optschanged(void)
9699#endif 9614#endif
9700} 9615}
9701 9616
9702static struct localvar *localvars; 9617struct localvar_list {
9618 struct localvar_list *next;
9619 struct localvar *lv;
9620};
9621
9622static struct localvar_list *localvar_stack;
9703 9623
9704/* 9624/*
9705 * Called after a function returns. 9625 * Called after a function returns.
9706 * Interrupts must be off. 9626 * Interrupts must be off.
9707 */ 9627 */
9708static void 9628static void
9709poplocalvars(void) 9629poplocalvars(int keep)
9710{ 9630{
9711 struct localvar *lvp; 9631 struct localvar_list *ll;
9632 struct localvar *lvp, *next;
9712 struct var *vp; 9633 struct var *vp;
9713 9634
9714 while ((lvp = localvars) != NULL) { 9635 INT_OFF;
9715 localvars = lvp->next; 9636 ll = localvar_stack;
9637 localvar_stack = ll->next;
9638
9639 next = ll->lv;
9640 free(ll);
9641
9642 while ((lvp = next) != NULL) {
9643 next = lvp->next;
9716 vp = lvp->vp; 9644 vp = lvp->vp;
9717 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-")); 9645 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
9718 if (vp == NULL) { /* $- saved */ 9646 if (keep) {
9647 int bits = VSTRFIXED;
9648
9649 if (lvp->flags != VUNSET) {
9650 if (vp->var_text == lvp->text)
9651 bits |= VTEXTFIXED;
9652 else if (!(lvp->flags & (VTEXTFIXED|VSTACK)))
9653 free((char*)lvp->text);
9654 }
9655
9656 vp->flags &= ~bits;
9657 vp->flags |= (lvp->flags & bits);
9658
9659 if ((vp->flags &
9660 (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET)
9661 unsetvar(vp->var_text);
9662 } else if (vp == NULL) { /* $- saved */
9719 memcpy(optlist, lvp->text, sizeof(optlist)); 9663 memcpy(optlist, lvp->text, sizeof(optlist));
9720 free((char*)lvp->text); 9664 free((char*)lvp->text);
9721 optschanged(); 9665 optschanged();
9722 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { 9666 } else if (lvp->flags == VUNSET) {
9667 vp->flags &= ~(VSTRFIXED|VREADONLY);
9723 unsetvar(vp->var_text); 9668 unsetvar(vp->var_text);
9724 } else { 9669 } else {
9725 if (vp->var_func) 9670 if (vp->var_func)
@@ -9731,19 +9676,43 @@ poplocalvars(void)
9731 } 9676 }
9732 free(lvp); 9677 free(lvp);
9733 } 9678 }
9679 INT_ON;
9680}
9681
9682/*
9683 * Create a new localvar environment.
9684 */
9685static struct localvar_list *
9686pushlocalvars(void)
9687{
9688 struct localvar_list *ll;
9689
9690 INT_OFF;
9691 ll = ckzalloc(sizeof(*ll));
9692 /*ll->lv = NULL; - zalloc did it */
9693 ll->next = localvar_stack;
9694 localvar_stack = ll;
9695 INT_ON;
9696
9697 return ll->next;
9698}
9699
9700static void
9701unwindlocalvars(struct localvar_list *stop)
9702{
9703 while (localvar_stack != stop)
9704 poplocalvars(0);
9734} 9705}
9735 9706
9736static int 9707static int
9737evalfun(struct funcnode *func, int argc, char **argv, int flags) 9708evalfun(struct funcnode *func, int argc, char **argv, int flags)
9738{ 9709{
9739 volatile struct shparam saveparam; 9710 volatile struct shparam saveparam;
9740 struct localvar *volatile savelocalvars;
9741 struct jmploc *volatile savehandler; 9711 struct jmploc *volatile savehandler;
9742 struct jmploc jmploc; 9712 struct jmploc jmploc;
9743 int e; 9713 int e;
9744 9714
9745 saveparam = shellparam; 9715 saveparam = shellparam;
9746 savelocalvars = localvars;
9747 savehandler = exception_handler; 9716 savehandler = exception_handler;
9748 e = setjmp(jmploc.loc); 9717 e = setjmp(jmploc.loc);
9749 if (e) { 9718 if (e) {
@@ -9751,7 +9720,6 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
9751 } 9720 }
9752 INT_OFF; 9721 INT_OFF;
9753 exception_handler = &jmploc; 9722 exception_handler = &jmploc;
9754 localvars = NULL;
9755 shellparam.malloced = 0; 9723 shellparam.malloced = 0;
9756 func->count++; 9724 func->count++;
9757 funcnest++; 9725 funcnest++;
@@ -9762,13 +9730,13 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
9762 shellparam.optind = 1; 9730 shellparam.optind = 1;
9763 shellparam.optoff = -1; 9731 shellparam.optoff = -1;
9764#endif 9732#endif
9733 pushlocalvars();
9765 evaltree(func->n.narg.next, flags & EV_TESTED); 9734 evaltree(func->n.narg.next, flags & EV_TESTED);
9735 poplocalvars(0);
9766 funcdone: 9736 funcdone:
9767 INT_OFF; 9737 INT_OFF;
9768 funcnest--; 9738 funcnest--;
9769 freefunc(func); 9739 freefunc(func);
9770 poplocalvars();
9771 localvars = savelocalvars;
9772 freeparam(&shellparam); 9740 freeparam(&shellparam);
9773 shellparam = saveparam; 9741 shellparam = saveparam;
9774 exception_handler = savehandler; 9742 exception_handler = savehandler;
@@ -9797,7 +9765,7 @@ mklocal(char *name)
9797 * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x 9765 * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x
9798 * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x 9766 * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x
9799 */ 9767 */
9800 lvp = localvars; 9768 lvp = localvar_stack->lv;
9801 while (lvp) { 9769 while (lvp) {
9802 if (lvp->vp && varcmp(lvp->vp->var_text, name) == 0) { 9770 if (lvp->vp && varcmp(lvp->vp->var_text, name) == 0) {
9803 if (eq) 9771 if (eq)
@@ -9822,10 +9790,9 @@ mklocal(char *name)
9822 if (vp == NULL) { 9790 if (vp == NULL) {
9823 /* variable did not exist yet */ 9791 /* variable did not exist yet */
9824 if (eq) 9792 if (eq)
9825 setvareq(name, VSTRFIXED); 9793 vp = setvareq(name, VSTRFIXED);
9826 else 9794 else
9827 setvar(name, NULL, VSTRFIXED); 9795 vp = setvar(name, NULL, VSTRFIXED);
9828 vp = *vpp; /* the new variable */
9829 lvp->flags = VUNSET; 9796 lvp->flags = VUNSET;
9830 } else { 9797 } else {
9831 lvp->text = vp->var_text; 9798 lvp->text = vp->var_text;
@@ -9842,8 +9809,8 @@ mklocal(char *name)
9842 } 9809 }
9843 } 9810 }
9844 lvp->vp = vp; 9811 lvp->vp = vp;
9845 lvp->next = localvars; 9812 lvp->next = localvar_stack->lv;
9846 localvars = lvp; 9813 localvar_stack->lv = lvp;
9847 ret: 9814 ret:
9848 INT_ON; 9815 INT_ON;
9849} 9816}
@@ -9856,7 +9823,7 @@ localcmd(int argc UNUSED_PARAM, char **argv)
9856{ 9823{
9857 char *name; 9824 char *name;
9858 9825
9859 if (!funcnest) 9826 if (!localvar_stack)
9860 ash_msg_and_raise_error("not in a function"); 9827 ash_msg_and_raise_error("not in a function");
9861 9828
9862 argv = argptr; 9829 argv = argptr;
@@ -10025,7 +9992,7 @@ static const struct builtincmd builtintab[] = {
10025#if ENABLE_FEATURE_SH_MATH 9992#if ENABLE_FEATURE_SH_MATH
10026 { BUILTIN_NOSPEC "let" , letcmd }, 9993 { BUILTIN_NOSPEC "let" , letcmd },
10027#endif 9994#endif
10028 { BUILTIN_ASSIGN "local" , localcmd }, 9995 { BUILTIN_SPEC_REG_ASSG "local" , localcmd },
10029#if ENABLE_ASH_PRINTF 9996#if ENABLE_ASH_PRINTF
10030 { BUILTIN_REGULAR "printf" , printfcmd }, 9997 { BUILTIN_REGULAR "printf" , printfcmd },
10031#endif 9998#endif
@@ -10115,6 +10082,7 @@ evalcommand(union node *cmd, int flags)
10115 static const struct builtincmd null_bltin = { 10082 static const struct builtincmd null_bltin = {
10116 "\0\0", bltincmd /* why three NULs? */ 10083 "\0\0", bltincmd /* why three NULs? */
10117 }; 10084 };
10085 struct localvar_list *localvar_stop;
10118 struct stackmark smark; 10086 struct stackmark smark;
10119 union node *argp; 10087 union node *argp;
10120 struct arglist arglist; 10088 struct arglist arglist;
@@ -10136,6 +10104,7 @@ evalcommand(union node *cmd, int flags)
10136 /* First expand the arguments. */ 10104 /* First expand the arguments. */
10137 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); 10105 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
10138 setstackmark(&smark); 10106 setstackmark(&smark);
10107 localvar_stop = pushlocalvars();
10139 back_exitstatus = 0; 10108 back_exitstatus = 0;
10140 10109
10141 cmdentry.cmdtype = CMDBUILTIN; 10110 cmdentry.cmdtype = CMDBUILTIN;
@@ -10189,6 +10158,8 @@ evalcommand(union node *cmd, int flags)
10189 spp = varlist.lastp; 10158 spp = varlist.lastp;
10190 expandarg(argp, &varlist, EXP_VARTILDE); 10159 expandarg(argp, &varlist, EXP_VARTILDE);
10191 10160
10161 mklocal((*spp)->text);
10162
10192 /* 10163 /*
10193 * Modify the command lookup path, if a PATH= assignment 10164 * Modify the command lookup path, if a PATH= assignment
10194 * is present 10165 * is present
@@ -10354,17 +10325,12 @@ evalcommand(union node *cmd, int flags)
10354 /* NOTREACHED */ 10325 /* NOTREACHED */
10355 } /* default */ 10326 } /* default */
10356 case CMDBUILTIN: 10327 case CMDBUILTIN:
10357 cmdenviron = varlist.list; 10328 if (spclbltin > 0 || argc == 0) {
10358 if (cmdenviron) { 10329 poplocalvars(1);
10359 struct strlist *list = cmdenviron; 10330 if (cmd_is_exec && argc > 1)
10360 int i = VNOSET; 10331 listsetvar(varlist.list, VEXPORT);
10361 if (spclbltin > 0 || argc == 0) {
10362 i = 0;
10363 if (cmd_is_exec && argc > 1)
10364 i = VEXPORT;
10365 }
10366 listsetvar(list, i);
10367 } 10332 }
10333
10368 /* Tight loop with builtins only: 10334 /* Tight loop with builtins only:
10369 * "while kill -0 $child; do true; done" 10335 * "while kill -0 $child; do true; done"
10370 * will never exit even if $child died, unless we do this 10336 * will never exit even if $child died, unless we do this
@@ -10382,7 +10348,7 @@ evalcommand(union node *cmd, int flags)
10382 goto readstatus; 10348 goto readstatus;
10383 10349
10384 case CMDFUNCTION: 10350 case CMDFUNCTION:
10385 listsetvar(varlist.list, 0); 10351 poplocalvars(1);
10386 /* See above for the rationale */ 10352 /* See above for the rationale */
10387 dowait(DOWAIT_NONBLOCK, NULL); 10353 dowait(DOWAIT_NONBLOCK, NULL);
10388 if (evalfun(cmdentry.u.func, argc, argv, flags)) 10354 if (evalfun(cmdentry.u.func, argc, argv, flags))
@@ -10395,6 +10361,7 @@ evalcommand(union node *cmd, int flags)
10395 out: 10361 out:
10396 if (cmd->ncmd.redirect) 10362 if (cmd->ncmd.redirect)
10397 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec); 10363 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
10364 unwindlocalvars(localvar_stop);
10398 if (lastarg) { 10365 if (lastarg) {
10399 /* dsl: I think this is intended to be used to support 10366 /* dsl: I think this is intended to be used to support
10400 * '_' in 'vi' command mode during line editing... 10367 * '_' in 'vi' command mode during line editing...
@@ -13051,6 +13018,12 @@ expandstr(const char *ps)
13051 return stackblock(); 13018 return stackblock();
13052} 13019}
13053 13020
13021static inline int
13022parser_eof(void)
13023{
13024 return tokpushback && lasttoken == TEOF;
13025}
13026
13054/* 13027/*
13055 * Execute a command or commands contained in a string. 13028 * Execute a command or commands contained in a string.
13056 */ 13029 */
@@ -13086,7 +13059,7 @@ evalstring(char *s, int flags)
13086 while ((n = parsecmd(0)) != NODE_EOF) { 13059 while ((n = parsecmd(0)) != NODE_EOF) {
13087 int i; 13060 int i;
13088 13061
13089 i = evaltree(n, flags); 13062 i = evaltree(n, flags & ~(parser_eof() ? 0 : EV_EXIT));
13090 if (n) 13063 if (n)
13091 status = i; 13064 status = i;
13092 popstackmark(&smark); 13065 popstackmark(&smark);
@@ -13237,11 +13210,12 @@ dotcmd(int argc_ UNUSED_PARAM, char **argv_ UNUSED_PARAM)
13237 char *fullname; 13210 char *fullname;
13238 char **argv; 13211 char **argv;
13239 char *args_need_save; 13212 char *args_need_save;
13240 struct strlist *sp;
13241 volatile struct shparam saveparam; 13213 volatile struct shparam saveparam;
13242 13214
13243 for (sp = cmdenviron; sp; sp = sp->next) 13215//???
13244 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED); 13216// struct strlist *sp;
13217// for (sp = cmdenviron; sp; sp = sp->next)
13218// setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
13245 13219
13246 nextopt(nullstr); /* handle possible "--" */ 13220 nextopt(nullstr); /* handle possible "--" */
13247 argv = argptr; 13221 argv = argptr;
@@ -13588,13 +13562,18 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13588 return 0; 13562 return 0;
13589 } 13563 }
13590 13564
13565 /* Why the second check?
13566 * "trap NUM [sig2]..." is the same as "trap - NUM [sig2]..."
13567 * In this case, NUM is signal no, not an action.
13568 */
13591 action = NULL; 13569 action = NULL;
13592 if (ap[1]) 13570 if (ap[1] && !is_number(ap[0]))
13593 action = *ap++; 13571 action = *ap++;
13572
13594 exitcode = 0; 13573 exitcode = 0;
13595 while (*ap) { 13574 while (*ap) {
13596 signo = get_signum(*ap); 13575 signo = get_signum(*ap);
13597 if (signo < 0 || signo >= NSIG) { 13576 if (signo < 0) {
13598 /* Mimic bash message exactly */ 13577 /* Mimic bash message exactly */
13599 ash_msg("%s: invalid signal specification", *ap); 13578 ash_msg("%s: invalid signal specification", *ap);
13600 exitcode = 1; 13579 exitcode = 1;
@@ -13752,7 +13731,6 @@ unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13752 char **ap; 13731 char **ap;
13753 int i; 13732 int i;
13754 int flag = 0; 13733 int flag = 0;
13755 int ret = 0;
13756 13734
13757 while ((i = nextopt("vf")) != 0) { 13735 while ((i = nextopt("vf")) != 0) {
13758 flag = i; 13736 flag = i;
@@ -13760,15 +13738,13 @@ unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13760 13738
13761 for (ap = argptr; *ap; ap++) { 13739 for (ap = argptr; *ap; ap++) {
13762 if (flag != 'f') { 13740 if (flag != 'f') {
13763 i = unsetvar(*ap); 13741 unsetvar(*ap);
13764 ret |= i; 13742 continue;
13765 if (!(i & 2))
13766 continue;
13767 } 13743 }
13768 if (flag != 'v') 13744 if (flag != 'v')
13769 unsetfunc(*ap); 13745 unsetfunc(*ap);
13770 } 13746 }
13771 return ret & 1; 13747 return 0;
13772} 13748}
13773 13749
13774static const unsigned char timescmd_str[] ALIGN1 = { 13750static const unsigned char timescmd_str[] ALIGN1 = {
@@ -14250,6 +14226,9 @@ reset(void)
14250 /* from redir.c: */ 14226 /* from redir.c: */
14251 while (redirlist) 14227 while (redirlist)
14252 popredir(/*drop:*/ 0, /*restore:*/ 0); 14228 popredir(/*drop:*/ 0, /*restore:*/ 0);
14229
14230 /* from var.c: */
14231 unwindlocalvars(NULL);
14253} 14232}
14254 14233
14255#if PROFILE 14234#if PROFILE
@@ -14386,7 +14365,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14386 // if (!sflag) g_parsefile->pf_fd = -1; 14365 // if (!sflag) g_parsefile->pf_fd = -1;
14387 // ^^ not necessary since now we special-case fd 0 14366 // ^^ not necessary since now we special-case fd 0
14388 // in is_hidden_fd() to not be considered "hidden fd" 14367 // in is_hidden_fd() to not be considered "hidden fd"
14389 evalstring(minusc, 0); 14368 evalstring(minusc, sflag ? 0 : EV_EXIT);
14390 } 14369 }
14391 14370
14392 if (sflag || minusc == NULL) { 14371 if (sflag || minusc == NULL) {
diff --git a/shell/ash_test/ash-misc/assignment2.right b/shell/ash_test/ash-misc/assignment2.right
new file mode 100644
index 000000000..179c71c5a
--- /dev/null
+++ b/shell/ash_test/ash-misc/assignment2.right
@@ -0,0 +1,2 @@
1./assignment2.tests: line 2: a=b: not found
2127
diff --git a/shell/ash_test/ash-misc/assignment2.tests b/shell/ash_test/ash-misc/assignment2.tests
new file mode 100755
index 000000000..f6938434c
--- /dev/null
+++ b/shell/ash_test/ash-misc/assignment2.tests
@@ -0,0 +1,3 @@
1# This must not be interpreted as an assignment
2a''=b true
3echo $?
diff --git a/shell/ash_test/ash-misc/empty_args.right b/shell/ash_test/ash-misc/empty_args.right
new file mode 100644
index 000000000..968b5a4d9
--- /dev/null
+++ b/shell/ash_test/ash-misc/empty_args.right
@@ -0,0 +1,6 @@
1Null 0th arg:
2./empty_args.tests: line 2: : Permission denied
3127
4Null 1st arg:
50
6Null arg in exec:
diff --git a/shell/ash_test/ash-misc/empty_args.tests b/shell/ash_test/ash-misc/empty_args.tests
new file mode 100755
index 000000000..efce5494a
--- /dev/null
+++ b/shell/ash_test/ash-misc/empty_args.tests
@@ -0,0 +1,9 @@
1echo Null 0th arg:
2""
3echo $?
4echo Null 1st arg:
5# printf without args would print usage info
6printf ""
7echo $?
8echo Null arg in exec:
9exec printf ""
diff --git a/shell/ash_test/ash-misc/env_and_func.right b/shell/ash_test/ash-misc/env_and_func.right
new file mode 100644
index 000000000..5fc3488ae
--- /dev/null
+++ b/shell/ash_test/ash-misc/env_and_func.right
@@ -0,0 +1,2 @@
1var=val
2var=val
diff --git a/shell/ash_test/ash-misc/env_and_func.tests b/shell/ash_test/ash-misc/env_and_func.tests
new file mode 100755
index 000000000..3efef1a41
--- /dev/null
+++ b/shell/ash_test/ash-misc/env_and_func.tests
@@ -0,0 +1,8 @@
1var=old
2f() { echo "var=$var"; }
3# bash: POSIXLY_CORRECT behavior is to "leak" new variable values
4# out of function invocations (similar to "special builtins" behavior);
5# but in "bash mode", they don't leak.
6# hush does not "leak" values. ash does.
7var=val f
8echo "var=$var"
diff --git a/shell/ash_test/ash-psubst/emptytick.right b/shell/ash_test/ash-psubst/emptytick.right
new file mode 100644
index 000000000..7629deba6
--- /dev/null
+++ b/shell/ash_test/ash-psubst/emptytick.right
@@ -0,0 +1,17 @@
10
20
3./emptytick.tests: line 3: : Permission denied
4127
5./emptytick.tests: line 4: : Permission denied
6127
70
80
90
100
11./emptytick.tests: line 10: : Permission denied
12127
13./emptytick.tests: line 11: : Permission denied
14127
150
160
17./emptytick.tests: exec: line 15: : Permission denied
diff --git a/shell/ash_test/ash-psubst/emptytick.tests b/shell/ash_test/ash-psubst/emptytick.tests
new file mode 100755
index 000000000..eaffafb22
--- /dev/null
+++ b/shell/ash_test/ash-psubst/emptytick.tests
@@ -0,0 +1,16 @@
1true; ``; echo $?
2false; ``; echo $?
3true; `""`; echo $?
4false; `""`; echo $?
5true; ` `; echo $?
6false; ` `; echo $?
7
8true; $(); echo $?
9false; $(); echo $?
10true; $(""); echo $?
11false; $(""); echo $?
12true; $( ); echo $?
13false; $( ); echo $?
14
15exec ''; echo $?
16echo Not reached
diff --git a/shell/ash_test/ash-psubst/tick.right b/shell/ash_test/ash-psubst/tick.right
new file mode 100644
index 000000000..6ed281c75
--- /dev/null
+++ b/shell/ash_test/ash-psubst/tick.right
@@ -0,0 +1,2 @@
11
21
diff --git a/shell/ash_test/ash-psubst/tick.tests b/shell/ash_test/ash-psubst/tick.tests
new file mode 100755
index 000000000..1f749a9cd
--- /dev/null
+++ b/shell/ash_test/ash-psubst/tick.tests
@@ -0,0 +1,4 @@
1true
2false; echo `echo $?`
3true
4{ false; echo `echo $?`; }
diff --git a/shell/ash_test/ash-psubst/tick2.right b/shell/ash_test/ash-psubst/tick2.right
new file mode 100644
index 000000000..216c883b8
--- /dev/null
+++ b/shell/ash_test/ash-psubst/tick2.right
@@ -0,0 +1 @@
BAZ
diff --git a/shell/ash_test/ash-psubst/tick2.tests b/shell/ash_test/ash-psubst/tick2.tests
new file mode 100755
index 000000000..db4e944fe
--- /dev/null
+++ b/shell/ash_test/ash-psubst/tick2.tests
@@ -0,0 +1,5 @@
1if false; then
2 echo "FOO"
3 tmp=`echo BAR >&2`
4fi
5echo BAZ
diff --git a/shell/ash_test/ash-psubst/tick3.right b/shell/ash_test/ash-psubst/tick3.right
new file mode 100644
index 000000000..00f267ae5
--- /dev/null
+++ b/shell/ash_test/ash-psubst/tick3.right
@@ -0,0 +1,6 @@
1\TESTZZBEST
2$TEST
3Q
4a\bc
511-$a-\t-\-\"-`-\--\z-\*-\?-22 33-$a-\t-\-"-`-\--\z-\*-\?-44
6done:0
diff --git a/shell/ash_test/ash-psubst/tick3.tests b/shell/ash_test/ash-psubst/tick3.tests
new file mode 100755
index 000000000..3aeb241c3
--- /dev/null
+++ b/shell/ash_test/ash-psubst/tick3.tests
@@ -0,0 +1,14 @@
1test "$CONFIG_FEATURE_FANCY_ECHO" = "y" || exit 77
2
3TEST=Q
4# \` is special
5echo `echo '\'TEST\`echo ZZ\`BEST`
6# \$ and \\ are special
7echo `echo \\$TEST`
8echo `echo \$TEST`
9echo a`echo \\\\b`c
10
11# \" is not special if in unquoted `cmd` (passed verbatim WITH \),
12# but is special in quoted one
13echo `echo 11'-$a-\t-\\-\"-\`-\--\z-\*-\?-'22` "`echo 33'-$a-\t-\\-\"-\`-\--\z-\*-\?-'44`"
14echo done:$?
diff --git a/shell/ash_test/ash-psubst/tick4.right b/shell/ash_test/ash-psubst/tick4.right
new file mode 100644
index 000000000..d8030eafd
--- /dev/null
+++ b/shell/ash_test/ash-psubst/tick4.right
@@ -0,0 +1,7 @@
1(TEST) BEST
2TEST) BEST
3((TEST) BEST
4)
5abc
6a)c
7OK: 0
diff --git a/shell/ash_test/ash-psubst/tick4.tests b/shell/ash_test/ash-psubst/tick4.tests
new file mode 100755
index 000000000..f2305fb3d
--- /dev/null
+++ b/shell/ash_test/ash-psubst/tick4.tests
@@ -0,0 +1,7 @@
1echo $(echo '(TEST)' BEST)
2echo $(echo 'TEST)' BEST)
3echo $(echo \(\(TEST\) BEST)
4echo $(echo \))
5echo $(echo a"`echo "b"`"c )
6echo $(echo a"`echo ")"`"c )
7echo OK: $?
diff --git a/shell/ash_test/ash-psubst/tick_huge.right b/shell/ash_test/ash-psubst/tick_huge.right
new file mode 100644
index 000000000..11740f674
--- /dev/null
+++ b/shell/ash_test/ash-psubst/tick_huge.right
@@ -0,0 +1,3 @@
1546ed3f5c81c780d3ab86ada14824237 -
2546ed3f5c81c780d3ab86ada14824237 -
3End
diff --git a/shell/ash_test/ash-psubst/tick_huge.tests b/shell/ash_test/ash-psubst/tick_huge.tests
new file mode 100755
index 000000000..acce92fb2
--- /dev/null
+++ b/shell/ash_test/ash-psubst/tick_huge.tests
@@ -0,0 +1,7 @@
1# This creates 120k file
2yes "123456789 123456789 123456789 123456789" | head -3000 >>"$0.tmp"
3
4echo "`cat $0.tmp`" | md5sum
5rm "$0.tmp"
6yes "123456789 123456789 123456789 123456789" | head -3000 | md5sum
7echo End
diff --git a/shell/ash_test/ash-signals/catch.right b/shell/ash_test/ash-signals/catch.right
new file mode 100644
index 000000000..68530c6e7
--- /dev/null
+++ b/shell/ash_test/ash-signals/catch.right
@@ -0,0 +1,5 @@
1sending USR2
2caught
3sending USR2
4sending USR2
5User defined signal 2
diff --git a/shell/ash_test/ash-signals/catch.tests b/shell/ash_test/ash-signals/catch.tests
new file mode 100755
index 000000000..d2a21d17e
--- /dev/null
+++ b/shell/ash_test/ash-signals/catch.tests
@@ -0,0 +1,20 @@
1# avoid ugly warnings about signals not being caught
2trap ":" USR1 USR2
3
4"$THIS_SH" -c '
5trap "echo caught" USR2
6echo "sending USR2"
7kill -USR2 $$
8
9trap "" USR2
10echo "sending USR2"
11kill -USR2 $$
12
13trap "-" USR2
14echo "sending USR2"
15kill -USR2 $$
16
17echo "not reached"
18'
19
20trap "-" USR1 USR2
diff --git a/shell/ash_test/ash-signals/signal_read2.right b/shell/ash_test/ash-signals/signal_read2.right
new file mode 100644
index 000000000..87d8da304
--- /dev/null
+++ b/shell/ash_test/ash-signals/signal_read2.right
@@ -0,0 +1,2 @@
1Hangup
2Done:129
diff --git a/shell/ash_test/ash-signals/signal_read2.tests b/shell/ash_test/ash-signals/signal_read2.tests
new file mode 100755
index 000000000..eab5b9b5b
--- /dev/null
+++ b/shell/ash_test/ash-signals/signal_read2.tests
@@ -0,0 +1,7 @@
1$THIS_SH -c '
2(sleep 1; kill -HUP $$) &
3while true; do
4 read ignored
5done
6'
7echo "Done:$?"
diff --git a/shell/ash_test/ash-signals/subshell.right b/shell/ash_test/ash-signals/subshell.right
new file mode 100644
index 000000000..248fcc41a
--- /dev/null
+++ b/shell/ash_test/ash-signals/subshell.right
@@ -0,0 +1,21 @@
1trap -- '' HUP
2trap -- '' QUIT
3trap -- '' SYS
4Ok
5trap -- '' HUP
6trap -- '' QUIT
7trap -- '' SYS
8Ok
9trap -- '' HUP
10trap -- '' QUIT
11trap -- '' SYS
12Ok
13trap -- '' HUP
14trap -- '' QUIT
15trap -- '' SYS
16Ok
17trap -- '' HUP
18trap -- '' QUIT
19trap -- '' SYS
20Terminated
21Done
diff --git a/shell/ash_test/ash-signals/subshell.tests b/shell/ash_test/ash-signals/subshell.tests
new file mode 100755
index 000000000..d877f2b82
--- /dev/null
+++ b/shell/ash_test/ash-signals/subshell.tests
@@ -0,0 +1,19 @@
1# Non-empty traps should be reset in subshell
2
3# HUP is special in interactive shells
4trap '' HUP
5# QUIT is always special
6trap '' QUIT
7# SYS is not special
8trap '' SYS
9# WINCH is harmless
10trap 'bad: caught WINCH' WINCH
11# With TERM we'll check whether it is reset
12trap 'bad: caught TERM' TERM
13
14(trap; "$THIS_SH" -c 'kill -HUP $PPID'; echo Ok)
15(trap; "$THIS_SH" -c 'kill -QUIT $PPID'; echo Ok)
16(trap; "$THIS_SH" -c 'kill -SYS $PPID'; echo Ok)
17(trap; "$THIS_SH" -c 'kill -WINCH $PPID'; echo Ok)
18(trap; "$THIS_SH" -c 'kill -TERM $PPID'; echo Bad: TERM is not reset)
19echo Done
diff --git a/shell/ash_test/ash-vars/param_expand_alt.right b/shell/ash_test/ash-vars/param_expand_alt.right
new file mode 100644
index 000000000..1303f8064
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_expand_alt.right
@@ -0,0 +1,9 @@
1SHELL: line 1: syntax error: bad substitution
2SHELL: line 1: syntax error: bad substitution
3__
4_z_ _z_
5_ _ _ _ _
6_aaaa _ _ _word _word
7_ _ _ _ _
8_ _ _ _word _
9_fff _ _ _word _word
diff --git a/shell/ash_test/ash-vars/param_expand_alt.tests b/shell/ash_test/ash-vars/param_expand_alt.tests
new file mode 100755
index 000000000..23e9a26be
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_expand_alt.tests
@@ -0,0 +1,33 @@
1# First try some invalid patterns. Do in subshell due to parsing error.
2# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages)
3"$THIS_SH" -c 'echo ${+} ; echo moo' SHELL
4"$THIS_SH" -c 'echo ${:+} ; echo moo' SHELL
5
6# now some funky ones.
7# ${V+word} "if V unset, then substitute nothing, else substitute word"
8# ${V:+word} "if V unset or '', then substitute nothing, else substitute word"
9#
10# ${#:+} is a :+ op on $#, but ${#+} (and any other ${#c}) is "length of $c",
11# not + op on $#.
12# bash and dash do not accept ${#+}. it's possible for some shell to skip
13# the check that c is valid and interpret ${#+} as "len of $+". Not testing it.
14# echo _${#+}_
15echo _${#:+}_
16# Forms with non-empty word work as expected in both ash and bash.
17echo _${#+z}_ _${#:+z}_
18
19# now some valid ones
20set --
21echo _$1 _${1+} _${1:+} _${1+word} _${1:+word}
22
23set -- aaaa
24echo _$1 _${1+} _${1:+} _${1+word} _${1:+word}
25
26unset f
27echo _$f _${f+} _${f:+} _${f+word} _${f:+word}
28
29f=
30echo _$f _${f+} _${f:+} _${f+word} _${f:+word}
31
32f=fff
33echo _$f _${f+} _${f:+} _${f+word} _${f:+word}
diff --git a/shell/ash_test/ash-vars/param_expand_assign.right b/shell/ash_test/ash-vars/param_expand_assign.right
new file mode 100644
index 000000000..9b07d8cd4
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_expand_assign.right
@@ -0,0 +1,27 @@
1SHELL: line 1: syntax error: bad substitution
2SHELL: line 1: syntax error: bad substitution
30
40
5SHELL: line 1: 1: bad variable name
6SHELL: line 1: 1: bad variable name
7SHELL: line 1: 1: bad variable name
8SHELL: line 1: 1: bad variable name
9_aa
10_aa
11_aa
12_aa
13_
14_
15_
16_word
17_word
18_
19_
20_
21_
22_word
23_fff
24_fff
25_fff
26_fff
27_fff
diff --git a/shell/ash_test/ash-vars/param_expand_assign.tests b/shell/ash_test/ash-vars/param_expand_assign.tests
new file mode 100755
index 000000000..79de95613
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_expand_assign.tests
@@ -0,0 +1,39 @@
1# First try some invalid patterns. Do in subshell due to parsing error.
2# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages)
3"$THIS_SH" -c 'echo ${=}' SHELL
4"$THIS_SH" -c 'echo ${:=}' SHELL
5
6# now some funky ones
7"$THIS_SH" -c 'echo ${#=}' SHELL
8"$THIS_SH" -c 'echo ${#:=}' SHELL
9
10# should error out
11"$THIS_SH" -c 'set --; echo _${1=}' SHELL
12"$THIS_SH" -c 'set --; echo _${1:=}' SHELL
13"$THIS_SH" -c 'set --; echo _${1=word}' SHELL
14"$THIS_SH" -c 'set --; echo _${1:=word}' SHELL
15
16# should not error
17"$THIS_SH" -c 'set aa; echo _${1=}' SHELL
18"$THIS_SH" -c 'set aa; echo _${1:=}' SHELL
19"$THIS_SH" -c 'set aa; echo _${1=word}' SHELL
20"$THIS_SH" -c 'set aa; echo _${1:=word}' SHELL
21
22# should work fine
23unset f; echo _$f
24unset f; echo _${f=}
25unset f; echo _${f:=}
26unset f; echo _${f=word}
27unset f; echo _${f:=word}
28
29f=; echo _$f
30f=; echo _${f=}
31f=; echo _${f:=}
32f=; echo _${f=word}
33f=; echo _${f:=word}
34
35f=fff; echo _$f
36f=fff; echo _${f=}
37f=fff; echo _${f:=}
38f=fff; echo _${f=word}
39f=fff; echo _${f:=word}
diff --git a/shell/ash_test/ash-vars/param_expand_bash_substring.right b/shell/ash_test/ash-vars/param_expand_bash_substring.right
new file mode 100644
index 000000000..9ad6dbcad
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_expand_bash_substring.right
@@ -0,0 +1,64 @@
1SHELL: line 1: syntax error: bad substitution
2SHELL: line 1: syntax error: bad substitution
3SHELL: line 1: syntax error: bad substitution
4SHELL: line 1: syntax error: bad substitution
5SHELL: line 1: syntax error: missing '}'
61 =||
71:1 =||
81:1:2=||
91::2 =||
101:1: =||
111:: =||
121 =|0123|
131:1 =|123|
141:1:2=|12|
151::2 =|01|
161:1: =||
171:: =||
18f =||
19f:1 =||
20f:1:2=||
21f::2 =||
22f:1: =||
23f:: =||
24f =||
25f:1 =||
26f:1:2=||
27f::2 =||
28f:1: =||
29f:: =||
30f =|a|
31f:1 =||
32f:1:2=||
33f::2 =|a|
34f:1: =||
35f:: =||
36f =|0123456789|
37f:1 =|123456789|
38f:1:2=|12|
39f::2 =|01|
40f:1: =||
41f:: =||
42Substrings from special vars
43? =|0|
44?:1 =||
45?:1:2=||
46?::2 =|0|
47?:1: =||
48?:: =||
49# =|11|
50#:1 =|1|
51#:1:2=|1|
52#::2 =|11|
53#:1: =||
54#:: =||
55Substrings with expressions
56f =|01234567|
57f:1+1:2+2 =|2345|
58f:-1:2+2 =|01234567|
59f:1:f =|1234567|
60f:1:$f =|1234567|
61f:1:${f} =|1234567|
62f:1:${f:3:1} =|123|
63f:1:1`echo 1`=|1|
64Done
diff --git a/shell/ash_test/ash-vars/param_expand_bash_substring.tests b/shell/ash_test/ash-vars/param_expand_bash_substring.tests
new file mode 100755
index 000000000..cce9f123e
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_expand_bash_substring.tests
@@ -0,0 +1,84 @@
1# first try some invalid patterns
2# do all of these in subshells since it's supposed to error out
3# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages)
4export var=0123456789
5"$THIS_SH" -c 'echo ${:}' SHELL
6"$THIS_SH" -c 'echo ${::}' SHELL
7"$THIS_SH" -c 'echo ${:1}' SHELL
8"$THIS_SH" -c 'echo ${::1}' SHELL
9
10#this also is not valid in bash, hush accepts it:
11"$THIS_SH" -c 'echo ${var:}' SHELL
12
13# then some funky ones
14# UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}'
15
16# now some valid ones
17set --; echo "1 =|${1}|"
18set --; echo "1:1 =|${1:1}|"
19set --; echo "1:1:2=|${1:1:2}|"
20set --; echo "1::2 =|${1::2}|"
21set --; echo "1:1: =|${1:1:}|"
22set --; echo "1:: =|${1::}|"
23
24set -- 0123; echo "1 =|${1}|"
25set -- 0123; echo "1:1 =|${1:1}|"
26set -- 0123; echo "1:1:2=|${1:1:2}|"
27set -- 0123; echo "1::2 =|${1::2}|"
28set -- 0123; echo "1:1: =|${1:1:}|"
29set -- 0123; echo "1:: =|${1::}|"
30
31unset f; echo "f =|$f|"
32unset f; echo "f:1 =|${f:1}|"
33unset f; echo "f:1:2=|${f:1:2}|"
34unset f; echo "f::2 =|${f::2}|"
35unset f; echo "f:1: =|${f:1:}|"
36unset f; echo "f:: =|${f::}|"
37
38f=; echo "f =|$f|"
39f=; echo "f:1 =|${f:1}|"
40f=; echo "f:1:2=|${f:1:2}|"
41f=; echo "f::2 =|${f::2}|"
42f=; echo "f:1: =|${f:1:}|"
43f=; echo "f:: =|${f::}|"
44
45f=a; echo "f =|$f|"
46f=a; echo "f:1 =|${f:1}|"
47f=a; echo "f:1:2=|${f:1:2}|"
48f=a; echo "f::2 =|${f::2}|"
49f=a; echo "f:1: =|${f:1:}|"
50f=a; echo "f:: =|${f::}|"
51
52f=0123456789; echo "f =|$f|"
53f=0123456789; echo "f:1 =|${f:1}|"
54f=0123456789; echo "f:1:2=|${f:1:2}|"
55f=0123456789; echo "f::2 =|${f::2}|"
56f=0123456789; echo "f:1: =|${f:1:}|"
57f=0123456789; echo "f:: =|${f::}|"
58
59echo "Substrings from special vars"
60echo '? '"=|$?|"
61echo '?:1 '"=|${?:1}|"
62echo '?:1:2'"=|${?:1:2}|"
63echo '?::2 '"=|${?::2}|"
64echo '?:1: '"=|${?:1:}|"
65echo '?:: '"=|${?::}|"
66set -- 1 2 3 4 5 6 7 8 9 10 11
67echo '# '"=|$#|"
68echo '#:1 '"=|${#:1}|"
69echo '#:1:2'"=|${#:1:2}|"
70echo '#::2 '"=|${#::2}|"
71echo '#:1: '"=|${#:1:}|"
72echo '#:: '"=|${#::}|"
73
74echo "Substrings with expressions"
75f=01234567; echo 'f '"=|$f|"
76f=01234567; echo 'f:1+1:2+2 '"=|${f:1+1:2+2}|"
77f=01234567; echo 'f:-1:2+2 '"=|${f:-1:2+2}|"
78f=01234567; echo 'f:1:f '"=|${f:1:f}|"
79f=01234567; echo 'f:1:$f '"=|${f:1:$f}|"
80f=01234567; echo 'f:1:${f} '"=|${f:1:${f}}|"
81f=01234567; echo 'f:1:${f:3:1} '"=|${f:1:${f:3:1}}|"
82f=01234567; echo 'f:1:1`echo 1`'"=|${f:1:`echo 1`}|"
83
84echo Done
diff --git a/shell/ash_test/ash-vars/param_expand_default.right b/shell/ash_test/ash-vars/param_expand_default.right
new file mode 100644
index 000000000..3eecd1375
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_expand_default.right
@@ -0,0 +1,7 @@
1SHELL: line 1: syntax error: bad substitution
2_0 _0
3_ _ _ _word _word
4_aaaa _aaaa _aaaa _aaaa _aaaa
5_ _ _ _word _word
6_ _ _ _ _word
7_fff _fff _fff _fff _fff
diff --git a/shell/ash_test/ash-vars/param_expand_default.tests b/shell/ash_test/ash-vars/param_expand_default.tests
new file mode 100755
index 000000000..5e42d30e3
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_expand_default.tests
@@ -0,0 +1,23 @@
1# first try some invalid patterns (do in subshell due to parsing error)
2# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages)
3# valid in bash and ash (same as $-): "$THIS_SH" -c 'echo ${-}' SHELL
4"$THIS_SH" -c 'echo ${:-}' SHELL
5
6# now some funky ones
7echo _${#-} _${#:-}
8
9# now some valid ones
10set --
11echo _$1 _${1-} _${1:-} _${1-word} _${1:-word}
12
13set -- aaaa
14echo _$1 _${1-} _${1:-} _${1-word} _${1:-word}
15
16unset f
17echo _$f _${f-} _${f:-} _${f-word} _${f:-word}
18
19f=
20echo _$f _${f-} _${f:-} _${f-word} _${f:-word}
21
22f=fff
23echo _$f _${f-} _${f:-} _${f-word} _${f:-word}
diff --git a/shell/ash_test/ash-vars/param_expand_indicate_error.right b/shell/ash_test/ash-vars/param_expand_indicate_error.right
new file mode 100644
index 000000000..33afacee0
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_expand_indicate_error.right
@@ -0,0 +1,43 @@
1SHELL: line 1: syntax error: bad substitution
21
30
4====
5_
6SHELL: line 1: 1: parameter not set
7SHELL: line 1: 1: parameter not set or null
8SHELL: line 1: 1: message1
9SHELL: line 1: 1: message1
10SHELL: line 1: 1: unset!
11SHELL: line 1: 1: null or unset!
12====
13_aaaa
14_aaaa
15_aaaa
16_aaaa
17_aaaa
18_aaaa
19_aaaa
20====
21_
22SHELL: line 1: f: parameter not set
23SHELL: line 1: f: parameter not set or null
24SHELL: line 1: f: message3
25SHELL: line 1: f: message3
26SHELL: line 1: f: unset!
27SHELL: line 1: f: null or unset!
28====
29_
30_
31SHELL: line 1: f: parameter not set or null
32_
33SHELL: line 1: f: message4
34_
35SHELL: line 1: f: null or unset!
36====
37_fff
38_fff
39_fff
40_fff
41_fff
42_fff
43_fff
diff --git a/shell/ash_test/ash-vars/param_expand_indicate_error.tests b/shell/ash_test/ash-vars/param_expand_indicate_error.tests
new file mode 100755
index 000000000..0f3061949
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_expand_indicate_error.tests
@@ -0,0 +1,61 @@
1# do all of these in subshells since it's supposed to error out
2# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages)
3
4# first try some invalid patterns
5#"$THIS_SH" -c 'echo ${?}' SHELL -- this is valid as it's the same as $?
6"$THIS_SH" -c 'echo ${:?}' SHELL
7
8# then some funky ones
9# note: bash prints 1 - treats it as "length of $#"
10"$THIS_SH" -c 'echo ${#?}' SHELL
11# bash prints 0
12"$THIS_SH" -c 'echo ${#:?}' SHELL
13
14# now some valid ones
15export msg_unset="unset!"
16export msg_null_or_unset="null or unset!"
17
18echo ====
19"$THIS_SH" -c 'set --; echo _$1' SHELL
20"$THIS_SH" -c 'set --; echo _${1?}' SHELL
21"$THIS_SH" -c 'set --; echo _${1:?}' SHELL
22"$THIS_SH" -c 'set --; echo _${1?message1}' SHELL
23"$THIS_SH" -c 'set --; echo _${1:?message1}' SHELL
24"$THIS_SH" -c 'set --; echo _${1?$msg_unset}' SHELL
25"$THIS_SH" -c 'set --; echo _${1:?$msg_null_or_unset}' SHELL
26
27echo ====
28"$THIS_SH" -c 'set -- aaaa; echo _$1' SHELL
29"$THIS_SH" -c 'set -- aaaa; echo _${1?}' SHELL
30"$THIS_SH" -c 'set -- aaaa; echo _${1:?}' SHELL
31"$THIS_SH" -c 'set -- aaaa; echo _${1?word}' SHELL
32"$THIS_SH" -c 'set -- aaaa; echo _${1:?word}' SHELL
33"$THIS_SH" -c 'set -- aaaa; echo _${1?$msg_unset}' SHELL
34"$THIS_SH" -c 'set -- aaaa; echo _${1:?$msg_null_or_unset}' SHELL
35
36echo ====
37"$THIS_SH" -c 'unset f; echo _$f' SHELL
38"$THIS_SH" -c 'unset f; echo _${f?}' SHELL
39"$THIS_SH" -c 'unset f; echo _${f:?}' SHELL
40"$THIS_SH" -c 'unset f; echo _${f?message3}' SHELL
41"$THIS_SH" -c 'unset f; echo _${f:?message3}' SHELL
42"$THIS_SH" -c 'unset f; echo _${f?$msg_unset}' SHELL
43"$THIS_SH" -c 'unset f; echo _${f:?$msg_null_or_unset}' SHELL
44
45echo ====
46"$THIS_SH" -c 'f=; echo _$f' SHELL
47"$THIS_SH" -c 'f=; echo _${f?}' SHELL
48"$THIS_SH" -c 'f=; echo _${f:?}' SHELL
49"$THIS_SH" -c 'f=; echo _${f?word}' SHELL
50"$THIS_SH" -c 'f=; echo _${f:?message4}' SHELL
51"$THIS_SH" -c 'f=; echo _${f?$msg_unset}' SHELL
52"$THIS_SH" -c 'f=; echo _${f:?$msg_null_or_unset}' SHELL
53
54echo ====
55"$THIS_SH" -c 'f=fff; echo _$f' SHELL
56"$THIS_SH" -c 'f=fff; echo _${f?}' SHELL
57"$THIS_SH" -c 'f=fff; echo _${f:?}' SHELL
58"$THIS_SH" -c 'f=fff; echo _${f?word}' SHELL
59"$THIS_SH" -c 'f=fff; echo _${f:?word}' SHELL
60"$THIS_SH" -c 'f=fff; echo _${f?$msg_unset}' SHELL
61"$THIS_SH" -c 'f=fff; echo _${f:?$msg_null_or_unset}' SHELL
diff --git a/shell/ash_test/ash-vars/param_expand_len1.right b/shell/ash_test/ash-vars/param_expand_len1.right
new file mode 100644
index 000000000..dff3c7bb1
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_expand_len1.right
@@ -0,0 +1,11 @@
1One:1
2Two:2
3Three:3
4
5One:1
6Two:2
7Three:3
8
9Ok ${#$}: 0
10
11Ok ${#!}: 0
diff --git a/shell/ash_test/ash-vars/param_expand_len1.tests b/shell/ash_test/ash-vars/param_expand_len1.tests
new file mode 100755
index 000000000..e1beab320
--- /dev/null
+++ b/shell/ash_test/ash-vars/param_expand_len1.tests
@@ -0,0 +1,31 @@
1# ${#c} for any single char c means "length of $c", including all special vars
2
3false
4echo One:${#?}
5(exit 10)
6echo Two:${#?}
7(exit 100)
8echo Three:${#?}
9
10echo
11echo One:${##}
12set -- 1 2 3 4 5 6 7 8 9 0
13echo Two:${##}
14set -- 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \
15 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \
16 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \
17 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
18echo Three:${##}
19
20echo
21v=$$
22test "${#v}" = "${#$}"
23echo 'Ok ${#$}:' $?
24
25echo
26sleep 0 &
27v=$!
28test "${#v}" = "${#!}"
29echo 'Ok ${#!}:' $?
30
31# TODO: ${#-} ${#_}
diff --git a/shell/ash_test/ash-vars/readonly0.right b/shell/ash_test/ash-vars/readonly0.right
index f3a6bde9e..ecc4054f8 100644
--- a/shell/ash_test/ash-vars/readonly0.right
+++ b/shell/ash_test/ash-vars/readonly0.right
@@ -10,4 +10,4 @@ Fail:2
10./readonly0.tests: export: line 27: a: is read only 10./readonly0.tests: export: line 27: a: is read only
11Fail:2 11Fail:2
12 12
13Fail:1 13./readonly0.tests: unset: line 44: a: is read only
diff --git a/shell/ash_test/ash-vars/unset.right b/shell/ash_test/ash-vars/unset.right
new file mode 100644
index 000000000..77d5abe9e
--- /dev/null
+++ b/shell/ash_test/ash-vars/unset.right
@@ -0,0 +1,17 @@
1./unset.tests: unset: line 3: -: bad variable name
22
3./unset.tests: unset: line 5: illegal option -m
42
50
6___
70 f g
80 g
90
10___
110 f g
120
130 f g
140
15___
16./unset.tests: unset: line 36: VAR_RO: is read only
172 f g
diff --git a/shell/ash_test/ash-vars/unset.tests b/shell/ash_test/ash-vars/unset.tests
new file mode 100755
index 000000000..11b392744
--- /dev/null
+++ b/shell/ash_test/ash-vars/unset.tests
@@ -0,0 +1,40 @@
1# check invalid options are rejected
2# bash: in posix mode, aborts if non-interactive; using subshell to avoid that
3(unset -)
4echo $?
5(unset -m a b c)
6echo $?
7
8# check funky usage
9unset
10echo $?
11
12# check normal usage
13echo ___
14f=f g=g
15echo $? $f $g
16unset f
17echo $? $f $g
18unset g
19echo $? $f $g
20
21echo ___
22f=f g=g
23echo $? $f $g
24unset f g
25echo $? $f $g
26f=f g=g
27echo $? $f $g
28unset -v f g
29echo $? $f $g
30
31# check read only vars
32echo ___
33f=f g=g
34VAR_RO=1
35readonly VAR_RO
36(unset VAR_RO)
37echo $? $f $g
38# not testing "do variables survive error halfway through unset" since unset aborts
39# unset f VAR_RO g
40#echo $? $f $g
diff --git a/shell/hush.c b/shell/hush.c
index 309ed2139..d0225edb9 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -4466,6 +4466,8 @@ static int parse_dollar(o_string *as_string,
4466 case '@': /* args */ 4466 case '@': /* args */
4467 goto make_one_char_var; 4467 goto make_one_char_var;
4468 case '{': { 4468 case '{': {
4469 char len_single_ch;
4470
4469 o_addchr(dest, SPECIAL_VAR_SYMBOL); 4471 o_addchr(dest, SPECIAL_VAR_SYMBOL);
4470 4472
4471 ch = i_getch(input); /* eat '{' */ 4473 ch = i_getch(input); /* eat '{' */
@@ -4485,6 +4487,7 @@ static int parse_dollar(o_string *as_string,
4485 return 0; 4487 return 0;
4486 } 4488 }
4487 nommu_addchr(as_string, ch); 4489 nommu_addchr(as_string, ch);
4490 len_single_ch = ch;
4488 ch |= quote_mask; 4491 ch |= quote_mask;
4489 4492
4490 /* It's possible to just call add_till_closing_bracket() at this point. 4493 /* It's possible to just call add_till_closing_bracket() at this point.
@@ -4509,9 +4512,18 @@ static int parse_dollar(o_string *as_string,
4509 /* handle parameter expansions 4512 /* handle parameter expansions
4510 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 4513 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
4511 */ 4514 */
4512 if (!strchr(VAR_SUBST_OPS, ch)) /* ${var<bad_char>... */ 4515 if (!strchr(VAR_SUBST_OPS, ch)) { /* ${var<bad_char>... */
4513 goto bad_dollar_syntax; 4516 if (len_single_ch != '#'
4514 4517 /*|| !strchr(SPECIAL_VARS_STR, ch) - disallow errors like ${#+} ? */
4518 || i_peek(input) != '}'
4519 ) {
4520 goto bad_dollar_syntax;
4521 }
4522 /* else: it's "length of C" ${#C} op,
4523 * where C is a single char
4524 * special var name, e.g. ${#!}.
4525 */
4526 }
4515 /* Eat everything until closing '}' (or ':') */ 4527 /* Eat everything until closing '}' (or ':') */
4516 end_ch = '}'; 4528 end_ch = '}';
4517 if (BASH_SUBSTR 4529 if (BASH_SUBSTR
@@ -4568,6 +4580,7 @@ static int parse_dollar(o_string *as_string,
4568 } 4580 }
4569 break; 4581 break;
4570 } 4582 }
4583 len_single_ch = 0; /* it can't be ${#C} op */
4571 } 4584 }
4572 o_addchr(dest, SPECIAL_VAR_SYMBOL); 4585 o_addchr(dest, SPECIAL_VAR_SYMBOL);
4573 break; 4586 break;
@@ -5559,8 +5572,10 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5559 first_char = arg[0] = arg0 & 0x7f; 5572 first_char = arg[0] = arg0 & 0x7f;
5560 exp_op = 0; 5573 exp_op = 0;
5561 5574
5562 if (first_char == '#' /* ${#... */ 5575 if (first_char == '#' && arg[1] /* ${#...} but not ${#} */
5563 && arg[1] && !exp_saveptr /* not ${#} and not ${#<op_char>...} */ 5576 && (!exp_saveptr /* and ( not(${#<op_char>...}) */
5577 || (arg[2] == '\0' && strchr(SPECIAL_VARS_STR, arg[1])) /* or ${#C} "len of $C" ) */
5578 ) /* NB: skipping ^^^specvar check mishandles ${#::2} */
5564 ) { 5579 ) {
5565 /* It must be length operator: ${#var} */ 5580 /* It must be length operator: ${#var} */
5566 var++; 5581 var++;
@@ -5797,7 +5812,11 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5797 /* mimic bash message */ 5812 /* mimic bash message */
5798 die_if_script("%s: %s", 5813 die_if_script("%s: %s",
5799 var, 5814 var,
5800 exp_word[0] ? exp_word : "parameter null or not set" 5815 exp_word[0]
5816 ? exp_word
5817 : "parameter null or not set"
5818 /* ash has more specific messages, a-la: */
5819 /*: (exp_save == ':' ? "parameter null or not set" : "parameter not set")*/
5801 ); 5820 );
5802//TODO: how interactive bash aborts expansion mid-command? 5821//TODO: how interactive bash aborts expansion mid-command?
5803 } else { 5822 } else {
@@ -6643,8 +6662,18 @@ struct squirrel {
6643 /* moved_to = -1: fd was opened by redirect; close orig_fd after redir */ 6662 /* moved_to = -1: fd was opened by redirect; close orig_fd after redir */
6644}; 6663};
6645 6664
6665static struct squirrel *append_squirrel(struct squirrel *sq, int i, int orig, int moved)
6666{
6667 sq = xrealloc(sq, (i + 2) * sizeof(sq[0]));
6668 sq[i].orig_fd = orig;
6669 sq[i].moved_to = moved;
6670 sq[i+1].orig_fd = -1; /* end marker */
6671 return sq;
6672}
6673
6646static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) 6674static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd)
6647{ 6675{
6676 int moved_to;
6648 int i = 0; 6677 int i = 0;
6649 6678
6650 if (sq) while (sq[i].orig_fd >= 0) { 6679 if (sq) while (sq[i].orig_fd >= 0) {
@@ -6664,15 +6693,12 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd)
6664 i++; 6693 i++;
6665 } 6694 }
6666 6695
6667 sq = xrealloc(sq, (i + 2) * sizeof(sq[0]));
6668 sq[i].orig_fd = fd;
6669 /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */ 6696 /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */
6670 sq[i].moved_to = fcntl_F_DUPFD(fd, avoid_fd); 6697 moved_to = fcntl_F_DUPFD(fd, avoid_fd);
6671 debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, sq[i].moved_to); 6698 debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to);
6672 if (sq[i].moved_to < 0 && errno != EBADF) 6699 if (moved_to < 0 && errno != EBADF)
6673 xfunc_die(); 6700 xfunc_die();
6674 sq[i+1].orig_fd = -1; /* end marker */ 6701 return append_squirrel(sq, i, fd, moved_to);
6675 return sq;
6676} 6702}
6677 6703
6678/* fd: redirect wants this fd to be used (e.g. 3>file). 6704/* fd: redirect wants this fd to be used (e.g. 3>file).
@@ -6778,6 +6804,19 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp)
6778 */ 6804 */
6779 return 1; 6805 return 1;
6780 } 6806 }
6807 if (openfd == redir->rd_fd && sqp) {
6808 /* open() gave us precisely the fd we wanted.
6809 * This means that this fd was not busy
6810 * (not opened to anywhere).
6811 * Remember to close it on restore:
6812 */
6813 struct squirrel *sq = *sqp;
6814 int i = 0;
6815 if (sq) while (sq[i].orig_fd >= 0)
6816 i++;
6817 *sqp = append_squirrel(sq, i, openfd, -1); /* -1 = "it was closed" */
6818 debug_printf_redir("redir to previously closed fd %d\n", openfd);
6819 }
6781 } else { 6820 } else {
6782 /* "rd_fd<*>rd_dup" or "rd_fd<*>-" cases */ 6821 /* "rd_fd<*>rd_dup" or "rd_fd<*>-" cases */
6783 openfd = redir->rd_dup; 6822 openfd = redir->rd_dup;
@@ -9719,7 +9758,7 @@ static int FAST_FUNC builtin_trap(char **argv)
9719 sighandler_t handler; 9758 sighandler_t handler;
9720 9759
9721 sig = get_signum(*argv++); 9760 sig = get_signum(*argv++);
9722 if (sig < 0 || sig >= NSIG) { 9761 if (sig < 0) {
9723 ret = EXIT_FAILURE; 9762 ret = EXIT_FAILURE;
9724 /* Mimic bash message exactly */ 9763 /* Mimic bash message exactly */
9725 bb_error_msg("trap: %s: invalid signal specification", argv[-1]); 9764 bb_error_msg("trap: %s: invalid signal specification", argv[-1]);
diff --git a/shell/hush_test/hush-misc/env_and_func.tests b/shell/hush_test/hush-misc/env_and_func.tests
index 1d4eaf3a7..3efef1a41 100755
--- a/shell/hush_test/hush-misc/env_and_func.tests
+++ b/shell/hush_test/hush-misc/env_and_func.tests
@@ -1,4 +1,8 @@
1var=old 1var=old
2f() { echo "var=$var"; } 2f() { echo "var=$var"; }
3# bash: POSIXLY_CORRECT behavior is to "leak" new variable values
4# out of function invocations (similar to "special builtins" behavior);
5# but in "bash mode", they don't leak.
6# hush does not "leak" values. ash does.
3var=val f 7var=val f
4echo "var=$var" 8echo "var=$var"
diff --git a/shell/hush_test/hush-redir/redir.right b/shell/hush_test/hush-redir/redir.right
new file mode 100644
index 000000000..4de5ec701
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir.right
@@ -0,0 +1,2 @@
1hush: write error: Bad file descriptor
2TEST
diff --git a/shell/hush_test/hush-redir/redir.tests b/shell/hush_test/hush-redir/redir.tests
new file mode 100755
index 000000000..7a1a66806
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir.tests
@@ -0,0 +1,6 @@
1# test: closed fds should stay closed
2exec 1>&-
3echo TEST >TEST
4echo JUNK # lost: stdout is closed
5cat TEST >&2
6rm TEST
diff --git a/shell/hush_test/hush-vars/param_expand_alt.right b/shell/hush_test/hush-vars/param_expand_alt.right
index 67f18d69c..c46786e1f 100644
--- a/shell/hush_test/hush-vars/param_expand_alt.right
+++ b/shell/hush_test/hush-vars/param_expand_alt.right
@@ -1,6 +1,7 @@
1hush: syntax error: unterminated ${name} 1hush: syntax error: unterminated ${name}
2hush: syntax error: unterminated ${name} 2hush: syntax error: unterminated ${name}
3__ __ 3__
4_z_ _z_
4_ _ _ _ _ 5_ _ _ _ _
5_aaaa _ _ _word _word 6_aaaa _ _ _word _word
6_ _ _ _ _ 7_ _ _ _ _
diff --git a/shell/hush_test/hush-vars/param_expand_alt.tests b/shell/hush_test/hush-vars/param_expand_alt.tests
index 3b646b142..23e9a26be 100755
--- a/shell/hush_test/hush-vars/param_expand_alt.tests
+++ b/shell/hush_test/hush-vars/param_expand_alt.tests
@@ -1,9 +1,20 @@
1# first try some invalid patterns (do in subshell due to parsing error) 1# First try some invalid patterns. Do in subshell due to parsing error.
2"$THIS_SH" -c 'echo ${+} ; echo moo' 2# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages)
3"$THIS_SH" -c 'echo ${:+} ; echo moo' 3"$THIS_SH" -c 'echo ${+} ; echo moo' SHELL
4"$THIS_SH" -c 'echo ${:+} ; echo moo' SHELL
4 5
5# now some funky ones. (bash doesn't accept ${#+}) 6# now some funky ones.
6echo _${#+}_ _${#:+}_ 7# ${V+word} "if V unset, then substitute nothing, else substitute word"
8# ${V:+word} "if V unset or '', then substitute nothing, else substitute word"
9#
10# ${#:+} is a :+ op on $#, but ${#+} (and any other ${#c}) is "length of $c",
11# not + op on $#.
12# bash and dash do not accept ${#+}. it's possible for some shell to skip
13# the check that c is valid and interpret ${#+} as "len of $+". Not testing it.
14# echo _${#+}_
15echo _${#:+}_
16# Forms with non-empty word work as expected in both ash and bash.
17echo _${#+z}_ _${#:+z}_
7 18
8# now some valid ones 19# now some valid ones
9set -- 20set --
diff --git a/shell/hush_test/hush-vars/param_expand_assign.tests b/shell/hush_test/hush-vars/param_expand_assign.tests
index 149cb20df..79de95613 100755
--- a/shell/hush_test/hush-vars/param_expand_assign.tests
+++ b/shell/hush_test/hush-vars/param_expand_assign.tests
@@ -1,22 +1,23 @@
1# first try some invalid patterns (do in subshell due to parsing error) 1# First try some invalid patterns. Do in subshell due to parsing error.
2"$THIS_SH" -c 'echo ${=}' 2# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages)
3"$THIS_SH" -c 'echo ${:=}' 3"$THIS_SH" -c 'echo ${=}' SHELL
4"$THIS_SH" -c 'echo ${:=}' SHELL
4 5
5# now some funky ones 6# now some funky ones
6"$THIS_SH" -c 'echo ${#=}' 7"$THIS_SH" -c 'echo ${#=}' SHELL
7"$THIS_SH" -c 'echo ${#:=}' 8"$THIS_SH" -c 'echo ${#:=}' SHELL
8 9
9# should error out 10# should error out
10"$THIS_SH" -c 'set --; echo _${1=}' 11"$THIS_SH" -c 'set --; echo _${1=}' SHELL
11"$THIS_SH" -c 'set --; echo _${1:=}' 12"$THIS_SH" -c 'set --; echo _${1:=}' SHELL
12"$THIS_SH" -c 'set --; echo _${1=word}' 13"$THIS_SH" -c 'set --; echo _${1=word}' SHELL
13"$THIS_SH" -c 'set --; echo _${1:=word}' 14"$THIS_SH" -c 'set --; echo _${1:=word}' SHELL
14 15
15# should not error 16# should not error
16"$THIS_SH" -c 'set aa; echo _${1=}' 17"$THIS_SH" -c 'set aa; echo _${1=}' SHELL
17"$THIS_SH" -c 'set aa; echo _${1:=}' 18"$THIS_SH" -c 'set aa; echo _${1:=}' SHELL
18"$THIS_SH" -c 'set aa; echo _${1=word}' 19"$THIS_SH" -c 'set aa; echo _${1=word}' SHELL
19"$THIS_SH" -c 'set aa; echo _${1:=word}' 20"$THIS_SH" -c 'set aa; echo _${1:=word}' SHELL
20 21
21# should work fine 22# should work fine
22unset f; echo _$f 23unset f; echo _$f
diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.tests b/shell/hush_test/hush-vars/param_expand_bash_substring.tests
index 5c9552dba..cce9f123e 100755
--- a/shell/hush_test/hush-vars/param_expand_bash_substring.tests
+++ b/shell/hush_test/hush-vars/param_expand_bash_substring.tests
@@ -1,13 +1,14 @@
1# first try some invalid patterns 1# first try some invalid patterns
2# do all of these in subshells since it's supposed to error out 2# do all of these in subshells since it's supposed to error out
3# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages)
3export var=0123456789 4export var=0123456789
4"$THIS_SH" -c 'echo ${:}' 5"$THIS_SH" -c 'echo ${:}' SHELL
5"$THIS_SH" -c 'echo ${::}' 6"$THIS_SH" -c 'echo ${::}' SHELL
6"$THIS_SH" -c 'echo ${:1}' 7"$THIS_SH" -c 'echo ${:1}' SHELL
7"$THIS_SH" -c 'echo ${::1}' 8"$THIS_SH" -c 'echo ${::1}' SHELL
8 9
9#this also is not valid in bash, but we accept it: 10#this also is not valid in bash, hush accepts it:
10"$THIS_SH" -c 'echo ${var:}' 11"$THIS_SH" -c 'echo ${var:}' SHELL
11 12
12# then some funky ones 13# then some funky ones
13# UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}' 14# UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}'
diff --git a/shell/hush_test/hush-vars/param_expand_default.tests b/shell/hush_test/hush-vars/param_expand_default.tests
index 1ea051748..16e5f8efe 100755
--- a/shell/hush_test/hush-vars/param_expand_default.tests
+++ b/shell/hush_test/hush-vars/param_expand_default.tests
@@ -1,6 +1,8 @@
1# first try some invalid patterns (do in subshell due to parsing error) 1# first try some invalid patterns (do in subshell due to parsing error)
2"$THIS_SH" -c 'echo ${-}' 2# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages)
3"$THIS_SH" -c 'echo ${:-}' 3# valid in bash and ash (same as $-), not supported in hush (yet?):
4"$THIS_SH" -c 'echo ${-}' SHELL
5"$THIS_SH" -c 'echo ${:-}' SHELL
4 6
5# now some funky ones 7# now some funky ones
6echo _${#-} _${#:-} 8echo _${#-} _${#:-}
diff --git a/shell/hush_test/hush-vars/param_expand_indicate_error.right b/shell/hush_test/hush-vars/param_expand_indicate_error.right
index 06fcc5104..acf293893 100644
--- a/shell/hush_test/hush-vars/param_expand_indicate_error.right
+++ b/shell/hush_test/hush-vars/param_expand_indicate_error.right
@@ -1,5 +1,5 @@
1hush: syntax error: unterminated ${name} 1hush: syntax error: unterminated ${name}
20 21
30 30
4==== 4====
5_ 5_
diff --git a/shell/hush_test/hush-vars/param_expand_indicate_error.tests b/shell/hush_test/hush-vars/param_expand_indicate_error.tests
index be14b1e37..5f946e39a 100755
--- a/shell/hush_test/hush-vars/param_expand_indicate_error.tests
+++ b/shell/hush_test/hush-vars/param_expand_indicate_error.tests
@@ -5,7 +5,7 @@
5"$THIS_SH" -c 'echo ${:?}' 5"$THIS_SH" -c 'echo ${:?}'
6 6
7# then some funky ones 7# then some funky ones
8# note: bash prints 1 - treats it as "length of $#"? We print 0 8# note: bash prints 1 - treats it as "length of $#"
9"$THIS_SH" -c 'echo ${#?}' 9"$THIS_SH" -c 'echo ${#?}'
10# bash prints 0 10# bash prints 0
11"$THIS_SH" -c 'echo ${#:?}' 11"$THIS_SH" -c 'echo ${#:?}'
diff --git a/shell/hush_test/hush-vars/param_expand_len1.right b/shell/hush_test/hush-vars/param_expand_len1.right
new file mode 100644
index 000000000..dff3c7bb1
--- /dev/null
+++ b/shell/hush_test/hush-vars/param_expand_len1.right
@@ -0,0 +1,11 @@
1One:1
2Two:2
3Three:3
4
5One:1
6Two:2
7Three:3
8
9Ok ${#$}: 0
10
11Ok ${#!}: 0
diff --git a/shell/hush_test/hush-vars/param_expand_len1.tests b/shell/hush_test/hush-vars/param_expand_len1.tests
new file mode 100755
index 000000000..e1beab320
--- /dev/null
+++ b/shell/hush_test/hush-vars/param_expand_len1.tests
@@ -0,0 +1,31 @@
1# ${#c} for any single char c means "length of $c", including all special vars
2
3false
4echo One:${#?}
5(exit 10)
6echo Two:${#?}
7(exit 100)
8echo Three:${#?}
9
10echo
11echo One:${##}
12set -- 1 2 3 4 5 6 7 8 9 0
13echo Two:${##}
14set -- 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \
15 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \
16 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \
17 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
18echo Three:${##}
19
20echo
21v=$$
22test "${#v}" = "${#$}"
23echo 'Ok ${#$}:' $?
24
25echo
26sleep 0 &
27v=$!
28test "${#v}" = "${#!}"
29echo 'Ok ${#!}:' $?
30
31# TODO: ${#-} ${#_}
diff --git a/shell/hush_test/hush-vars/unset.right b/shell/hush_test/hush-vars/unset.right
index 1fbe76a73..097274201 100644
--- a/shell/hush_test/hush-vars/unset.right
+++ b/shell/hush_test/hush-vars/unset.right
@@ -12,7 +12,7 @@ ___
120 f g 120 f g
130 130
14___ 14___
15hush: HUSH_VERSION: readonly variable 15hush: VAR_RO: readonly variable
161 f g 161 f g
17hush: HUSH_VERSION: readonly variable 17hush: VAR_RO: readonly variable
181 181
diff --git a/shell/hush_test/hush-vars/unset.tests b/shell/hush_test/hush-vars/unset.tests
index f59ce5923..81243fbf9 100755
--- a/shell/hush_test/hush-vars/unset.tests
+++ b/shell/hush_test/hush-vars/unset.tests
@@ -1,4 +1,5 @@
1# check invalid options are rejected 1# check invalid options are rejected
2# bash: in posix mode, aborts if non-interactive
2unset - 3unset -
3echo $? 4echo $?
4unset -m a b c 5unset -m a b c
@@ -30,7 +31,9 @@ echo $? $f $g
30# check read only vars 31# check read only vars
31echo ___ 32echo ___
32f=f g=g 33f=f g=g
33unset HUSH_VERSION 34VAR_RO=1
35readonly VAR_RO
36unset VAR_RO
34echo $? $f $g 37echo $? $f $g
35unset f HUSH_VERSION g 38unset f VAR_RO g
36echo $? $f $g 39echo $? $f $g
diff --git a/testsuite/tar.tests b/testsuite/tar.tests
index 9f7ce1587..1675b07b1 100755
--- a/testsuite/tar.tests
+++ b/testsuite/tar.tests
@@ -10,9 +10,6 @@ unset LC_COLLATE
10unset LC_ALL 10unset LC_ALL
11umask 022 11umask 022
12 12
13rm -rf tar.tempdir 2>/dev/null
14mkdir tar.tempdir && cd tar.tempdir || exit 1
15
16# testing "test name" "script" "expected result" "file input" "stdin" 13# testing "test name" "script" "expected result" "file input" "stdin"
17 14
18testing "Empty file is not a tarball" '\ 15testing "Empty file is not a tarball" '\
@@ -53,6 +50,7 @@ dd if=/dev/zero bs=512 count=20 2>/dev/null | tar xvf - 2>&1; echo $?
53"" "" 50"" ""
54SKIP= 51SKIP=
55 52
53mkdir tar.tempdir && cd tar.tempdir || exit 1
56# "tar cf test.tar input input_dir/ input_hard1 input_hard2 input_hard1 input_dir/ input": 54# "tar cf test.tar input input_dir/ input_hard1 input_hard2 input_hard1 input_dir/ input":
57# GNU tar 1.26 records as hardlinks: 55# GNU tar 1.26 records as hardlinks:
58# input_hard2 -> input_hard1 56# input_hard2 -> input_hard1
@@ -64,7 +62,6 @@ SKIP=
64# We also don't use "hrw-r--r--" notation for hardlinks in "tar tv" listing. 62# We also don't use "hrw-r--r--" notation for hardlinks in "tar tv" listing.
65optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES 63optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES
66testing "tar hardlinks and repeated files" '\ 64testing "tar hardlinks and repeated files" '\
67rm -rf input_* test.tar 2>/dev/null
68>input_hard1 65>input_hard1
69ln input_hard1 input_hard2 66ln input_hard1 input_hard2
70mkdir input_dir 67mkdir input_dir
@@ -95,10 +92,11 @@ drwxr-xr-x input_dir
95" \ 92" \
96"" "" 93"" ""
97SKIP= 94SKIP=
95cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null
98 96
97mkdir tar.tempdir && cd tar.tempdir || exit 1
99optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES 98optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES
100testing "tar hardlinks mode" '\ 99testing "tar hardlinks mode" '\
101rm -rf input_* test.tar 2>/dev/null
102>input_hard1 100>input_hard1
103chmod 741 input_hard1 101chmod 741 input_hard1
104ln input_hard1 input_hard2 102ln input_hard1 input_hard2
@@ -128,10 +126,11 @@ Ok: 0
128" \ 126" \
129"" "" 127"" ""
130SKIP= 128SKIP=
129cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null
131 130
131mkdir tar.tempdir && cd tar.tempdir || exit 1
132optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES 132optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES
133testing "tar symlinks mode" '\ 133testing "tar symlinks mode" '\
134rm -rf input_* test.tar 2>/dev/null
135>input_file 134>input_file
136chmod 741 input_file 135chmod 741 input_file
137ln -s input_file input_soft 136ln -s input_file input_soft
@@ -159,10 +158,11 @@ lrwxrwxrwx input_file
159" \ 158" \
160"" "" 159"" ""
161SKIP= 160SKIP=
161cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null
162 162
163mkdir tar.tempdir && cd tar.tempdir || exit 1
163optional FEATURE_TAR_CREATE FEATURE_TAR_LONG_OPTIONS 164optional FEATURE_TAR_CREATE FEATURE_TAR_LONG_OPTIONS
164testing "tar --overwrite" "\ 165testing "tar --overwrite" "\
165rm -rf input_* test.tar 2>/dev/null
166ln input input_hard 166ln input input_hard
167tar cf test.tar input_hard 167tar cf test.tar input_hard
168echo WRONG >input 168echo WRONG >input
@@ -174,12 +174,13 @@ Ok
174" \ 174" \
175"Ok\n" "" 175"Ok\n" ""
176SKIP= 176SKIP=
177cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null
177 178
179mkdir tar.tempdir && cd tar.tempdir || exit 1
178test x"$SKIP_KNOWN_BUGS" = x"" && { 180test x"$SKIP_KNOWN_BUGS" = x"" && {
179# Needs to be run under non-root for meaningful test 181# Needs to be run under non-root for meaningful test
180optional FEATURE_TAR_CREATE 182optional FEATURE_TAR_CREATE
181testing "tar writing into read-only dir" '\ 183testing "tar writing into read-only dir" '\
182rm -rf input_* test.tar 2>/dev/null
183mkdir input_dir 184mkdir input_dir
184>input_dir/input_file 185>input_dir/input_file
185chmod 550 input_dir 186chmod 550 input_dir
@@ -201,7 +202,9 @@ dr-xr-x--- input_dir
201"" "" 202"" ""
202SKIP= 203SKIP=
203} 204}
205cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null
204 206
207mkdir tar.tempdir && cd tar.tempdir || exit 1
205# Had a bug where on extract autodetect first "switched off" -z 208# Had a bug where on extract autodetect first "switched off" -z
206# and then failed to recognize .tgz extension 209# and then failed to recognize .tgz extension
207optional FEATURE_TAR_CREATE FEATURE_SEAMLESS_GZ GUNZIP 210optional FEATURE_TAR_CREATE FEATURE_SEAMLESS_GZ GUNZIP
@@ -217,7 +220,9 @@ Ok
217" \ 220" \
218"" "" 221"" ""
219SKIP= 222SKIP=
223cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null
220 224
225mkdir tar.tempdir && cd tar.tempdir || exit 1
221# Do we detect XZ-compressed data (even w/o .tar.xz or txz extension)? 226# Do we detect XZ-compressed data (even w/o .tar.xz or txz extension)?
222# (the uuencoded hello_world.txz contains one empty file named "hello_world") 227# (the uuencoded hello_world.txz contains one empty file named "hello_world")
223optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_XZ 228optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_XZ
@@ -236,7 +241,9 @@ AAAEWVo=
236==== 241====
237" 242"
238SKIP= 243SKIP=
244cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null
239 245
246mkdir tar.tempdir && cd tar.tempdir || exit 1
240# On extract, everything up to and including last ".." component is stripped 247# On extract, everything up to and including last ".." component is stripped
241optional FEATURE_TAR_CREATE 248optional FEATURE_TAR_CREATE
242testing "tar strips /../ on extract" "\ 249testing "tar strips /../ on extract" "\
@@ -255,7 +262,9 @@ Ok
255" \ 262" \
256"" "" 263"" ""
257SKIP= 264SKIP=
265cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null
258 266
267mkdir tar.tempdir && cd tar.tempdir || exit 1
259# attack.tar.bz2 has symlink pointing to a system file 268# attack.tar.bz2 has symlink pointing to a system file
260# followed by a regular file with the same name 269# followed by a regular file with the same name
261# containing "root::0:0::/root:/bin/sh": 270# containing "root::0:0::/root:/bin/sh":
@@ -270,6 +279,7 @@ optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2
270testing "tar does not extract into symlinks" "\ 279testing "tar does not extract into symlinks" "\
271>>/tmp/passwd && uudecode -o input && tar xf input 2>&1 && rm passwd; cat /tmp/passwd; echo \$? 280>>/tmp/passwd && uudecode -o input && tar xf input 2>&1 && rm passwd; cat /tmp/passwd; echo \$?
272" "\ 281" "\
282tar: can't create symlink 'passwd' to '/tmp/passwd'
2730 2830
274" \ 284" \
275"" "\ 285"" "\
@@ -281,12 +291,15 @@ l4/V8LDoe90yiWJhOJvIypgEfxdyRThQkBVn/bI=
281==== 291====
282" 292"
283SKIP= 293SKIP=
294cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null
295
296mkdir tar.tempdir && cd tar.tempdir || exit 1
284# And same with -k 297# And same with -k
285optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2 298optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2
286testing "tar -k does not extract into symlinks" "\ 299testing "tar -k does not extract into symlinks" "\
287>>/tmp/passwd && uudecode -o input && tar xf input -k 2>&1 && rm passwd; cat /tmp/passwd; echo \$? 300>>/tmp/passwd && uudecode -o input && tar xf input -k 2>&1 && rm passwd; cat /tmp/passwd; echo \$?
288" "\ 301" "\
289tar: can't open 'passwd': File exists 302tar: can't create symlink 'passwd' to '/tmp/passwd'
2900 3030
291" \ 304" \
292"" "\ 305"" "\
@@ -298,7 +311,9 @@ l4/V8LDoe90yiWJhOJvIypgEfxdyRThQkBVn/bI=
298==== 311====
299" 312"
300SKIP= 313SKIP=
314cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null
301 315
316mkdir tar.tempdir && cd tar.tempdir || exit 1
302optional UNICODE_SUPPORT FEATURE_TAR_GNU_EXTENSIONS FEATURE_SEAMLESS_BZ2 FEATURE_TAR_AUTODETECT 317optional UNICODE_SUPPORT FEATURE_TAR_GNU_EXTENSIONS FEATURE_SEAMLESS_BZ2 FEATURE_TAR_AUTODETECT
303testing "Pax-encoded UTF8 names and symlinks" '\ 318testing "Pax-encoded UTF8 names and symlinks" '\
304tar xvf ../tar.utf8.tar.bz2 2>&1; echo $? 319tar xvf ../tar.utf8.tar.bz2 2>&1; echo $?
@@ -318,8 +333,36 @@ etc/ssl/certs/f80cc7f6.0 -> EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem
318" \ 333" \
319"" "" 334"" ""
320SKIP= 335SKIP=
336cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null
321 337
322 338mkdir tar.tempdir && cd tar.tempdir || exit 1
323cd .. && rm -rf tar.tempdir || exit 1 339optional FEATURE_SEAMLESS_BZ2 FEATURE_TAR_AUTODETECT
340testing "Symlink attack: create symlink and then write through it" '\
341exec 2>&1
342uudecode -o input && tar xvf input; echo $?
343ls /tmp/bb_test_evilfile
344ls bb_test_evilfile
345ls symlink/bb_test_evilfile
346' "\
347anything.txt
348symlink
349symlink/bb_test_evilfile
350tar: can't create symlink 'symlink' to '/tmp'
3511
352ls: /tmp/bb_test_evilfile: No such file or directory
353ls: bb_test_evilfile: No such file or directory
354symlink/bb_test_evilfile
355" \
356"" "\
357begin-base64 644 tar_symlink_attack.tar.bz2
358QlpoOTFBWSZTWZgs7bQAALT/hMmQAFBAAf+AEMAGJPPv32AAAIAIMAC5thlR
359omAjAmCMADQT1BqNE0AEwAAjAEwElTKeo9NTR6h6gaeoA0DQNLVdwZZ5iNTk
360AQwCAV6S00QFJYhrlfFkVCEDEGtgNVqYrI0uK3ggnt30gqk4e1TTQm5QIAKa
361SJqzRGSFLMmOloHSAcvLiFxxRiQtQZF+qPxbo173ZDISOAoNoPN4PQPhBhKS
362n8fYaKlioCTzL2oXYczyUUIP4u5IpwoSEwWdtoA=
363====
364"
365SKIP=
366cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null
324 367
325exit $FAILCOUNT 368exit $FAILCOUNT
diff --git a/util-linux/blkid.c b/util-linux/blkid.c
index f353cf248..0bd701aae 100644
--- a/util-linux/blkid.c
+++ b/util-linux/blkid.c
@@ -13,8 +13,6 @@
13//config: select VOLUMEID 13//config: select VOLUMEID
14//config: help 14//config: help
15//config: Lists labels and UUIDs of all filesystems. 15//config: Lists labels and UUIDs of all filesystems.
16//config: WARNING:
17//config: With all submodules selected, it will add ~8k to busybox.
18//config: 16//config:
19//config:config FEATURE_BLKID_TYPE 17//config:config FEATURE_BLKID_TYPE
20//config: bool "Print filesystem type" 18//config: bool "Print filesystem type"
diff --git a/util-linux/findfs.c b/util-linux/findfs.c
index 359da581f..1102eeff5 100644
--- a/util-linux/findfs.c
+++ b/util-linux/findfs.c
@@ -14,8 +14,6 @@
14//config: select VOLUMEID 14//config: select VOLUMEID
15//config: help 15//config: help
16//config: Prints the name of a filesystem with given label or UUID. 16//config: Prints the name of a filesystem with given label or UUID.
17//config: WARNING:
18//config: With all submodules selected, it will add ~8k to busybox.
19 17
20/* Benefits from suid root: better access to /dev/BLOCKDEVs: */ 18/* Benefits from suid root: better access to /dev/BLOCKDEVs: */
21//applet:IF_FINDFS(APPLET(findfs, BB_DIR_SBIN, BB_SUID_MAYBE)) 19//applet:IF_FINDFS(APPLET(findfs, BB_DIR_SBIN, BB_SUID_MAYBE))
diff --git a/util-linux/getopt.c b/util-linux/getopt.c
index cd5679cff..cf1bc592f 100644
--- a/util-linux/getopt.c
+++ b/util-linux/getopt.c
@@ -41,7 +41,7 @@
41//config: wisely leave this disabled. 41//config: wisely leave this disabled.
42//config: 42//config:
43//config:config FEATURE_GETOPT_LONG 43//config:config FEATURE_GETOPT_LONG
44//config: bool "Support option -l" 44//config: bool "Support -l LONGOPTs"
45//config: default y if LONG_OPTS 45//config: default y if LONG_OPTS
46//config: depends on GETOPT 46//config: depends on GETOPT
47//config: help 47//config: help
diff --git a/util-linux/mdev.c b/util-linux/mdev.c
index 23b6f8285..8acc4d21d 100644
--- a/util-linux/mdev.c
+++ b/util-linux/mdev.c
@@ -56,7 +56,7 @@
56//config: For more information, please see docs/mdev.txt 56//config: For more information, please see docs/mdev.txt
57//config: 57//config:
58//config:config FEATURE_MDEV_LOAD_FIRMWARE 58//config:config FEATURE_MDEV_LOAD_FIRMWARE
59//config: bool "Support loading of firmwares" 59//config: bool "Support loading of firmware"
60//config: default y 60//config: default y
61//config: depends on MDEV 61//config: depends on MDEV
62//config: help 62//config: help
diff --git a/util-linux/mount.c b/util-linux/mount.c
index 1a39da2db..4d5c2243a 100644
--- a/util-linux/mount.c
+++ b/util-linux/mount.c
@@ -26,18 +26,17 @@
26//config: tree. The 'mount' utility is used to graft a filesystem onto a 26//config: tree. The 'mount' utility is used to graft a filesystem onto a
27//config: particular part of the tree. A filesystem can either live on a block 27//config: particular part of the tree. A filesystem can either live on a block
28//config: device, or it can be accessible over the network, as is the case with 28//config: device, or it can be accessible over the network, as is the case with
29//config: NFS filesystems. Most people using BusyBox will also want to enable 29//config: NFS filesystems.
30//config: the 'mount' utility.
31//config: 30//config:
32//config:config FEATURE_MOUNT_FAKE 31//config:config FEATURE_MOUNT_FAKE
33//config: bool "Support option -f" 32//config: bool "Support -f (fake mount)"
34//config: default y 33//config: default y
35//config: depends on MOUNT 34//config: depends on MOUNT
36//config: help 35//config: help
37//config: Enable support for faking a file system mount. 36//config: Enable support for faking a file system mount.
38//config: 37//config:
39//config:config FEATURE_MOUNT_VERBOSE 38//config:config FEATURE_MOUNT_VERBOSE
40//config: bool "Support option -v" 39//config: bool "Support -v (verbose)"
41//config: default y 40//config: default y
42//config: depends on MOUNT 41//config: depends on MOUNT
43//config: help 42//config: help
@@ -65,7 +64,6 @@
65//config: help 64//config: help
66//config: This allows for specifying a device by label or uuid, rather than by 65//config: This allows for specifying a device by label or uuid, rather than by
67//config: name. This feature utilizes the same functionality as blkid/findfs. 66//config: name. This feature utilizes the same functionality as blkid/findfs.
68//config: This also enables label or uuid support for swapon.
69//config: 67//config:
70//config:config FEATURE_MOUNT_NFS 68//config:config FEATURE_MOUNT_NFS
71//config: bool "Support mounting NFS file systems on Linux < 2.6.23" 69//config: bool "Support mounting NFS file systems on Linux < 2.6.23"
@@ -100,7 +98,7 @@
100//config: 98//config:
101//config:config FEATURE_MOUNT_FSTAB 99//config:config FEATURE_MOUNT_FSTAB
102//config: depends on MOUNT 100//config: depends on MOUNT
103//config: bool "Support /etc/fstab and -a" 101//config: bool "Support /etc/fstab and -a (mount all)"
104//config: default y 102//config: default y
105//config: help 103//config: help
106//config: Support mount all and looking for files in /etc/fstab. 104//config: Support mount all and looking for files in /etc/fstab.
diff --git a/util-linux/rdate.c b/util-linux/rdate.c
index 66b877e24..14ce591e9 100644
--- a/util-linux/rdate.c
+++ b/util-linux/rdate.c
@@ -41,7 +41,7 @@ static time_t askremotedate(const char *host)
41 uint32_t nett; 41 uint32_t nett;
42 int fd; 42 int fd;
43 43
44 /* Add a timeout for dead or inaccessible servers */ 44 /* Timeout for dead or inaccessible servers */
45 alarm(10); 45 alarm(10);
46 signal(SIGALRM, socket_timeout); 46 signal(SIGALRM, socket_timeout);
47 47
@@ -53,9 +53,8 @@ static time_t askremotedate(const char *host)
53 close(fd); 53 close(fd);
54 54
55 /* Convert from network byte order to local byte order. 55 /* Convert from network byte order to local byte order.
56 * RFC 868 time is the number of seconds 56 * RFC 868 time is seconds since 1900-01-01 00:00 GMT.
57 * since 00:00 (midnight) 1 January 1900 GMT 57 * RFC 868 time 2,208,988,800 corresponds to 1970-01-01 00:00 GMT.
58 * the RFC 868 time 2,208,988,800 corresponds to 00:00 1 Jan 1970 GMT
59 * Subtract the RFC 868 time to get Linux epoch. 58 * Subtract the RFC 868 time to get Linux epoch.
60 */ 59 */
61 nett = ntohl(nett) - RFC_868_BIAS; 60 nett = ntohl(nett) - RFC_868_BIAS;
@@ -66,7 +65,7 @@ static time_t askremotedate(const char *host)
66 * current time cur = 0x123ffffffff. 65 * current time cur = 0x123ffffffff.
67 * Assuming our time is not some 40 years off, 66 * Assuming our time is not some 40 years off,
68 * remote time must be 0x12400000001. 67 * remote time must be 0x12400000001.
69 * Need to adjust out time by (int32_t)(nett - cur). 68 * Need to adjust our time by (int32_t)(nett - cur).
70 */ 69 */
71 time_t cur = time(NULL); 70 time_t cur = time(NULL);
72 int32_t adjust = (int32_t)(nett - (uint32_t)cur); 71 int32_t adjust = (int32_t)(nett - (uint32_t)cur);
diff --git a/util-linux/swaponoff.c b/util-linux/swaponoff.c
index bda0687d6..f432ce180 100644
--- a/util-linux/swaponoff.c
+++ b/util-linux/swaponoff.c
@@ -37,6 +37,15 @@
37//config: bool "swapoff (4.3 kb)" 37//config: bool "swapoff (4.3 kb)"
38//config: default y 38//config: default y
39//config: select PLATFORM_LINUX 39//config: select PLATFORM_LINUX
40//config:
41//config:config FEATURE_SWAPONOFF_LABEL
42//config: bool "Support specifying devices by label or UUID"
43//config: default y
44//config: depends on SWAPON || SWAPOFF
45//config: select VOLUMEID
46//config: help
47//config: This allows for specifying a device by label or uuid, rather than by
48//config: name. This feature utilizes the same functionality as blkid/findfs.
40 49
41// APPLET_ODDNAME:name main location suid_type help 50// APPLET_ODDNAME:name main location suid_type help
42//applet:IF_SWAPON( APPLET_ODDNAME(swapon, swap_on_off, BB_DIR_SBIN, BB_SUID_DROP, swapon)) 51//applet:IF_SWAPON( APPLET_ODDNAME(swapon, swap_on_off, BB_DIR_SBIN, BB_SUID_DROP, swapon))
@@ -72,7 +81,7 @@
72# include <sys/swap.h> 81# include <sys/swap.h>
73#endif 82#endif
74 83
75#if ENABLE_FEATURE_MOUNT_LABEL 84#if ENABLE_FEATURE_SWAPONOFF_LABEL
76# include "volume_id.h" 85# include "volume_id.h"
77#else 86#else
78# define resolve_mount_spec(fsname) ((void)0) 87# define resolve_mount_spec(fsname) ((void)0)
diff --git a/util-linux/umount.c b/util-linux/umount.c
index 31bf671c1..122c0f579 100644
--- a/util-linux/umount.c
+++ b/util-linux/umount.c
@@ -18,7 +18,7 @@
18//config: utility, you almost certainly also want to enable 'umount'. 18//config: utility, you almost certainly also want to enable 'umount'.
19//config: 19//config:
20//config:config FEATURE_UMOUNT_ALL 20//config:config FEATURE_UMOUNT_ALL
21//config: bool "Support option -a" 21//config: bool "Support -a (unmount all)"
22//config: default y 22//config: default y
23//config: depends on UMOUNT 23//config: depends on UMOUNT
24//config: help 24//config: help
diff --git a/util-linux/volume_id/get_devname.c b/util-linux/volume_id/get_devname.c
index b64d28ceb..34f5d119f 100644
--- a/util-linux/volume_id/get_devname.c
+++ b/util-linux/volume_id/get_devname.c
@@ -11,6 +11,7 @@
11//kbuild:lib-$(CONFIG_BLKID) += get_devname.o 11//kbuild:lib-$(CONFIG_BLKID) += get_devname.o
12//kbuild:lib-$(CONFIG_FINDFS) += get_devname.o 12//kbuild:lib-$(CONFIG_FINDFS) += get_devname.o
13//kbuild:lib-$(CONFIG_FEATURE_MOUNT_LABEL) += get_devname.o 13//kbuild:lib-$(CONFIG_FEATURE_MOUNT_LABEL) += get_devname.o
14//kbuild:lib-$(CONFIG_FEATURE_SWAPONOFF_LABEL) += get_devname.o
14 15
15#include <sys/mount.h> /* BLKGETSIZE64 */ 16#include <sys/mount.h> /* BLKGETSIZE64 */
16#if !defined(BLKGETSIZE64) 17#if !defined(BLKGETSIZE64)
diff --git a/util-linux/volume_id/unused_minix.c b/util-linux/volume_id/minix.c
index 443dbc272..c934f9ead 100644
--- a/util-linux/volume_id/unused_minix.c
+++ b/util-linux/volume_id/minix.c
@@ -17,13 +17,12 @@
17 * License along with this library; if not, write to the Free Software 17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */ 19 */
20//config:config FEATURE_VOLUMEID_MINIX
21//config: bool "minix filesystem"
22//config: default y
23//config: depends on VOLUMEID
20 24
21//kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_MINIX) += minix.o 25//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_MINIX) += minix.o
22
23//config:### config FEATURE_VOLUMEID_MINIX
24//config:### bool "minix filesystem"
25//config:### default y
26//config:### depends on VOLUMEID
27 26
28#include "volume_id_internal.h" 27#include "volume_id_internal.h"
29 28
@@ -40,43 +39,53 @@ struct minix_super_block {
40 uint32_t s_zones; 39 uint32_t s_zones;
41} PACKED; 40} PACKED;
42 41
43#define MINIX_SUPERBLOCK_OFFSET 0x400 42/* V3 minix super-block data on disk */
43struct minix3_super_block {
44 uint32_t s_ninodes;
45 uint16_t s_pad0;
46 uint16_t s_imap_blocks;
47 uint16_t s_zmap_blocks;
48 uint16_t s_firstdatazone;
49 uint16_t s_log_zone_size;
50 uint16_t s_pad1;
51 uint32_t s_max_size;
52 uint32_t s_zones;
53 uint16_t s_magic;
54 uint16_t s_pad2;
55 uint16_t s_blocksize;
56 uint8_t s_disk_version;
57} PACKED;
58
59#define MINIX_SUPERBLOCK_OFFSET 0x400
44 60
45int FAST_FUNC volume_id_probe_minix(struct volume_id *id, uint64_t off) 61int FAST_FUNC volume_id_probe_minix(struct volume_id *id /*, uint64_t off*/)
46{ 62{
63#define off ((uint64_t)0)
47 struct minix_super_block *ms; 64 struct minix_super_block *ms;
65 struct minix3_super_block *ms3;
48 66
49 dbg("probing at offset 0x%llx", (unsigned long long) off); 67 dbg("probing at offset 0x%llx", (unsigned long long) off);
50 68
51 ms = volume_id_get_buffer(id, off + MINIX_SUPERBLOCK_OFFSET, 0x200); 69 ms = volume_id_get_buffer(id, off + MINIX_SUPERBLOCK_OFFSET, 0x200);
52 if (ms == NULL) 70 if (ms == NULL)
53 return -1; 71 return -1;
54 72 if (ms->s_magic == cpu_to_le16(0x137F)) /* minix V1 fs, 14 char names */
55 if (ms->s_magic == cpu_to_le16(0x137f)) {
56// id->type_version[0] = '1';
57 goto found; 73 goto found;
58 } 74 if (ms->s_magic == cpu_to_le16(0x138F)) /* minix V1 fs, 30 char names */
59
60 if (ms->s_magic == cpu_to_le16(0x1387)) {
61// id->type_version[0] = '1';
62 goto found; 75 goto found;
63 } 76 if (ms->s_magic == cpu_to_le16(0x2468)) /* minix V2 fs, 14 char names */
64 77 goto found;
65 if (ms->s_magic == cpu_to_le16(0x2468)) { 78 if (ms->s_magic == cpu_to_le16(0x2478)) /* minix V2 fs, 30 char names */
66// id->type_version[0] = '2';
67 goto found; 79 goto found;
68 }
69 80
70 if (ms->s_magic == cpu_to_le16(0x2478)) { 81 ms3 = (void*)ms;
71// id->type_version[0] = '2'; 82 if (ms3->s_magic == cpu_to_le16(0x4d5a)) /* minix V3 fs, 60 char names */
72 goto found; 83 goto found;
73 }
74 84
75 return -1; 85 return -1;
76 86
77 found: 87 found:
78// id->type_version[1] = '\0';
79// volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); 88// volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
80// id->type = "minix"; 89 IF_FEATURE_BLKID_TYPE(id->type = "minix";)
81 return 0; 90 return 0;
82} 91}
diff --git a/util-linux/volume_id/volume_id.c b/util-linux/volume_id/volume_id.c
index 5bb95994b..85315ced6 100644
--- a/util-linux/volume_id/volume_id.c
+++ b/util-linux/volume_id/volume_id.c
@@ -42,7 +42,6 @@
42#define ENABLE_FEATURE_VOLUMEID_VIARAID 0 42#define ENABLE_FEATURE_VOLUMEID_VIARAID 0
43 43
44/* These filesystems also have no label or uuid: */ 44/* These filesystems also have no label or uuid: */
45#define ENABLE_FEATURE_VOLUMEID_MINIX 0
46#define ENABLE_FEATURE_VOLUMEID_HPFS 0 45#define ENABLE_FEATURE_VOLUMEID_HPFS 0
47#define ENABLE_FEATURE_VOLUMEID_UFS 0 46#define ENABLE_FEATURE_VOLUMEID_UFS 0
48 47
diff --git a/util-linux/volume_id/volume_id_internal.h b/util-linux/volume_id/volume_id_internal.h
index 759a832e6..0eaea9b34 100644
--- a/util-linux/volume_id/volume_id_internal.h
+++ b/util-linux/volume_id/volume_id_internal.h
@@ -193,7 +193,7 @@ int FAST_FUNC volume_id_probe_luks(struct volume_id *id /*,uint64_t off*/);
193 193
194//int FAST_FUNC volume_id_probe_mac_partition_map(struct volume_id *id /*,uint64_t off*/); 194//int FAST_FUNC volume_id_probe_mac_partition_map(struct volume_id *id /*,uint64_t off*/);
195 195
196//int FAST_FUNC volume_id_probe_minix(struct volume_id *id /*,uint64_t off*/); 196int FAST_FUNC volume_id_probe_minix(struct volume_id *id /*, uint64_t off*/);
197 197
198//int FAST_FUNC volume_id_probe_msdos_part_table(struct volume_id *id /*,uint64_t off*/); 198//int FAST_FUNC volume_id_probe_msdos_part_table(struct volume_id *id /*,uint64_t off*/);
199 199