diff options
| author | Ron Yorston <rmy@pobox.com> | 2017-07-29 09:55:08 +0100 |
|---|---|---|
| committer | Ron Yorston <rmy@pobox.com> | 2017-07-29 09:55:08 +0100 |
| commit | 86d60bb0ceb277e500a8daabd995bc713bbdadc9 (patch) | |
| tree | 3e439f92d5a3fec2546d526579cc85e98f066e40 | |
| parent | b30c60a9786a1608211a96755996bd6c02951a27 (diff) | |
| parent | 69be994de69d794f038f10a3e7a67519b2006581 (diff) | |
| download | busybox-w32-86d60bb0ceb277e500a8daabd995bc713bbdadc9.tar.gz busybox-w32-86d60bb0ceb277e500a8daabd995bc713bbdadc9.tar.bz2 busybox-w32-86d60bb0ceb277e500a8daabd995bc713bbdadc9.zip | |
Merge branch 'busybox' into merge
101 files changed, 2017 insertions, 1101 deletions
| @@ -3,7 +3,7 @@ | |||
| 3 | # see scripts/kbuild/config-language.txt. | 3 | # see scripts/kbuild/config-language.txt. |
| 4 | # | 4 | # |
| 5 | 5 | ||
| 6 | mainmenu "BusyBox Configuration" | 6 | mainmenu "Configuration" |
| 7 | 7 | ||
| 8 | config HAVE_DOT_CONFIG | 8 | config HAVE_DOT_CONFIG |
| 9 | bool | 9 | bool |
| @@ -23,7 +23,7 @@ config PLATFORM_MINGW32 | |||
| 23 | 23 | ||
| 24 | endchoice | 24 | endchoice |
| 25 | 25 | ||
| 26 | menu "Busybox Settings" | 26 | menu "Settings" |
| 27 | 27 | ||
| 28 | config DESKTOP | 28 | config 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 | ||
| 90 | config FEATURE_COMPRESS_USAGE | 88 | config 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 | ||
| 115 | config GLOBBING | 112 | config 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 | ||
| 144 | config FEATURE_DEVPTS | 141 | config 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 | ||
| 322 | config BUSYBOX_EXEC_PATH | 319 | config 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 | ||
| 332 | config SELINUX | 329 | config SELINUX |
| 333 | bool "Support NSA Security Enhanced Linux" | 330 | bool "Support NSA Security Enhanced Linux" |
| @@ -390,21 +387,17 @@ config PLATFORM_LINUX | |||
| 390 | comment 'Build Options' | 387 | comment 'Build Options' |
| 391 | 388 | ||
| 392 | config STATIC | 389 | config 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 | ||
| 406 | config PIE | 399 | config 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 | ||
| 504 | config CROSS_COMPILER_PREFIX | 497 | config 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 | |||
| 616 | endchoice | 609 | endchoice |
| 617 | 610 | ||
| 618 | config PREFIX | 611 | config 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 | ||
| 624 | comment 'Debugging Options' | 617 | comment 'Debugging Options' |
| 625 | 618 | ||
| 626 | config DEBUG | 619 | config 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 | |||
| 648 | config DEBUG_SANITIZE | 644 | config 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 | ||
| 668 | config WERROR | 664 | config 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 | ||
| 281 | static 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 |
| 300 | static void writeLongname(int fd, int type, const char *name, int dir) | 299 | static 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 | |||
| 4 | true >anything.txt | ||
| 5 | tar cvf tar_symlink_attack.tar anything.txt | ||
| 6 | rm anything.txt | ||
| 7 | |||
| 8 | ln -s /tmp symlink | ||
| 9 | tar --append -f tar_symlink_attack.tar symlink | ||
| 10 | rm symlink | ||
| 11 | |||
| 12 | mkdir symlink | ||
| 13 | echo BUG >symlink/bb_test_evilfile | ||
| 14 | tar --append -f tar_symlink_attack.tar symlink/bb_test_evilfile | ||
| 15 | rm symlink/bb_test_evilfile | ||
| 16 | rmdir 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) \ |
| 119 | do { if (BB_BIG_ENDIAN) { \ | 119 | do { 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 | ||
| 38 | enum { | 36 | enum { |
| @@ -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 | |||
| 75 | static void doCommands(void); | ||
| 76 | static void subCommand(const char *cmd, int num1, int num2); | ||
| 77 | static int getNum(const char **retcp, smallint *retHaveNum, int *retNum); | ||
| 78 | static int setCurNum(int num); | ||
| 79 | static void addLines(int num); | ||
| 80 | static int insertLine(int num, const char *data, int len); | ||
| 81 | static void deleteLines(int num1, int num2); | ||
| 82 | static int printLines(int num1, int num2, int expandFlag); | ||
| 83 | static int writeLines(const char *file, int num1, int num2); | ||
| 84 | static int readLines(const char *file, int num); | ||
| 85 | static int searchLines(const char *str, int num1, int num2); | ||
| 86 | static LINE *findLine(int num); | ||
| 87 | static int findString(const LINE *lp, const char * str, int len, int offset); | ||
| 88 | |||
| 89 | |||
| 90 | static int bad_nums(int num1, int num2, const char *for_what) | 72 | static 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 | |||
| 100 | static char *skip_blank(const char *cp) | ||
| 101 | { | ||
| 102 | while (isblank(*cp)) | ||
| 103 | cp++; | ||
| 104 | return (char *)cp; | ||
| 105 | } | ||
| 106 | |||
| 107 | |||
| 108 | int ed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 109 | int 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 | */ | ||
| 136 | static 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 | */ |
| 348 | static void subCommand(const char *cmd, int num1, int num2) | 84 | static 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 | */ |
| 550 | static void addLines(int num) | 158 | static 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 | */ |
| 583 | static int getNum(const char **retcp, smallint *retHaveNum, int *retNum) | 202 | static 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 | */ | ||
| 293 | static 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 | */ | ||
| 312 | static 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 | */ | ||
| 352 | static 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 | */ | ||
| 873 | static 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 | */ |
| 957 | static NOINLINE int searchLines(const char *str, int num1, int num2) | 610 | static 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 | */ |
| 997 | static LINE *findLine(int num) | 778 | static 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 | 993 | int ed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | |
| 1038 | /* | 994 | int ed_main(int argc UNUSED_PARAM, char **argv) |
| 1039 | * Set the current line number. | ||
| 1040 | * Returns TRUE if successful. | ||
| 1041 | */ | ||
| 1042 | static 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 | ||
| 101 | controls a udhcpc instance which provides dhpc-assigned IP | 101 | controls a udhcpc instance which provides DHCP-assigned IP |
| 102 | address on interface named "if". Copy/rename this directory as needed to run | 102 | address on interface named "if". Copy/rename this directory as needed to run |
| 103 | udhcpc on other interfaces (var_service/dhcp_if/run script uses _foo suffix | 103 | udhcpc on other interfaces (var_service/dhcp_if/run script uses _foo suffix |
| 104 | of the parent directory as interface name). | 104 | of the parent directory as interface name). |
| @@ -164,9 +164,9 @@ This is achieved very simply by having | |||
| 164 | sv o . | 164 | sv o . |
| 165 | at the very beginning of fw/run script, not at the end. | 165 | at the very beginning of fw/run script, not at the end. |
| 166 | 166 | ||
| 167 | Therefore, any "sv u /var/run/service/fw" command by any other | 167 | Therefore, any "sv u fw" command by any other script "undoes" o(ne-shot) |
| 168 | script "undoes" o(ne-shot) command if fw still runs, thus | 168 | command if fw still runs, thus runsv will rerun it; or start it |
| 169 | runsv will rerun it; or start it in a normal way if fw is not running. | 169 | in a normal way if fw is not running. |
| 170 | 170 | ||
| 171 | This mechanism is the reason why fw is a service, not just a script. | 171 | This mechanism is the reason why fw is a service, not just a script. |
| 172 | 172 | ||
| @@ -198,7 +198,8 @@ PID TIME COMMAND | |||
| 198 | 568 0:00 svlogd -tt /var/log/service/dhcp_eth0 | 198 | 568 0:00 svlogd -tt /var/log/service/dhcp_eth0 |
| 199 | 850 0:00 udhcpc -vv --foreground --interface=eth0 | 199 | 850 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 | ||
| 202 | 563 0:00 runsv ntpd | 203 | 563 0:00 runsv ntpd |
| 203 | 573 0:01 svlogd -tt /var/log/service/ntpd | 204 | 573 0:01 svlogd -tt /var/log/service/ntpd |
| 204 | 845 0:00 busybox ntpd -dddnNl -S ./ntp.script -p 10.x.x.x -p 10.x.x.x | 205 | 845 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 | |||
| 233 | 622 0:00 busybox httpd -p80 -vvv -f -h /home/httpd_root | 234 | 622 0:00 busybox httpd -p80 -vvv -f -h /home/httpd_root |
| 234 | 577 0:00 runsv supplicant_wlan0 | 235 | 577 0:00 runsv supplicant_wlan0 |
| 235 | 627 0:00 svlogd -tt /var/log/service/supplicant_wlan0 | 236 | 627 0:00 svlogd -tt /var/log/service/supplicant_wlan0 |
| 236 | 638 0:03 wpa_supplicant -i wlan0 -c /var/service/supplicant_wlan0/wpa_supplicant.conf -d | 237 | 638 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 | |||
| 94 | It is not required that a specific clone should be used. Let evolution work. | 93 | It is not required that a specific clone should be used. Let evolution work. |
| 95 | 94 | ||
| 95 | |||
| 96 | Terminology | 96 | Terminology |
| 97 | 97 | ||
| 98 | daemon: any long running background program. Common examples are sshd, getty, | 98 | daemon: 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; | |||
| 198 | char get_header_cpio(archive_handle_t *archive_handle) FAST_FUNC; | 201 | char get_header_cpio(archive_handle_t *archive_handle) FAST_FUNC; |
| 199 | char get_header_tar(archive_handle_t *archive_handle) FAST_FUNC; | 202 | char get_header_tar(archive_handle_t *archive_handle) FAST_FUNC; |
| 200 | char get_header_tar_gz(archive_handle_t *archive_handle) FAST_FUNC; | 203 | char get_header_tar_gz(archive_handle_t *archive_handle) FAST_FUNC; |
| 204 | char get_header_tar_xz(archive_handle_t *archive_handle) FAST_FUNC; | ||
| 201 | char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC; | 205 | char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC; |
| 202 | char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC; | 206 | char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC; |
| 203 | char get_header_tar_xz(archive_handle_t *archive_handle) FAST_FUNC; | 207 | char 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 | ||
| 23 | config FEATURE_BUFFERS_USE_MALLOC | 22 | config 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 | ||
| 793 | static void run_applet_and_exit(const char *name, char **argv) NORETURN; | 799 | static void run_applet_and_exit(const char *name, char **argv) NORETURN; |
| 794 | 800 | ||
| 795 | /* If we were called as "busybox..." */ | 801 | # if ENABLE_BUSYBOX |
| 796 | static 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)))) | ||
| 813 | int busybox_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; | ||
| 814 | # else | ||
| 815 | # define busybox_main(argc,argv) busybox_main(argv) | ||
| 816 | static | ||
| 817 | # endif | ||
| 818 | int 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 | ||
| 6 | INSERT | ||
| 7 | |||
| 8 | config UDHCPD | 6 | config 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 | ||
| 16 | config 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 | |||
| 26 | config FEATURE_UDHCPD_BASE_IP_ON_MAC | 14 | config 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 | ||
| 28 | config 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 | |||
| 40 | config DHCPD_LEASES_FILE | 38 | config 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 | ||
| 64 | config UDHCPC | 62 | config 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: | ||
| 104 | INSERT | ||
| 105 | |||
| 106 | comment "Common options for DHCP applets" | ||
| 107 | depends on UDHCPD || UDHCPC || UDHCPC6 || DHCPRELAY | ||
| 108 | |||
| 105 | config FEATURE_UDHCP_PORT | 109 | config 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 | ||
| 113 | config UDHCP_DEBUG | 117 | config 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 | ||
| 125 | config 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 | |||
| 134 | config 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 | |||
| 142 | config UDHCPC_SLACK_FOR_BUGGY_SERVERS | 129 | config 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 | |||
| 152 | config 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 | |||
| 161 | config 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 | ||
| 348 | static void func_sid(char *buf, int size, const procps_status_t *ps) | ||
| 349 | { | ||
| 350 | sprintf(buf, "%*u", size, ps->sid); | ||
| 351 | } | ||
| 352 | |||
| 348 | static void put_lu(char *buf, int size, unsigned long u) | 353 | static 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 | /* |
| 433 | static void func_nice(char *buf, int size, const procps_status_t *ps) | ||
| 434 | { | ||
| 435 | ps->??? | ||
| 436 | } | ||
| 437 | |||
| 438 | static void func_pcpu(char *buf, int size, const procps_status_t *ps) | 438 | static 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 | ||
| 245 | typedef 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 */ | |||
| 1334 | static struct parsefile *g_parsefile = &basepf; /* current input file */ | 1337 | static struct parsefile *g_parsefile = &basepf; /* current input file */ |
| 1335 | static int startlinno; /* line # where last token started */ | 1338 | static int startlinno; /* line # where last token started */ |
| 1336 | static char *commandname; /* currently executing command */ | 1339 | static char *commandname; /* currently executing command */ |
| 1337 | static 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 | |||
| 1394 | static void raise_error_syntax(const char *) NORETURN; | 1408 | static void raise_error_syntax(const char *) NORETURN; |
| 1395 | static void | 1409 | static void |
| 1396 | raise_error_syntax(const char *msg) | 1410 | raise_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 | */ |
| 2315 | static const char * | 2326 | static ALWAYS_INLINE const char * |
| 2316 | bltinlookup(const char *name) | 2327 | bltinlookup(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 | */ |
| 2334 | static void | 2339 | static struct var * |
| 2335 | setvareq(char *s, int flags) | 2340 | setvareq(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 | */ |
| 2382 | static void | 2402 | static struct var * |
| 2383 | setvar(const char *name, const char *val, int flags) | 2403 | setvar(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 | ||
| 2415 | static void FAST_FUNC | 2438 | static 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 | */ |
| 2424 | static int | 2447 | static void |
| 2425 | unsetvar(const char *s) | 2448 | unsetvar(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 | |||
| 4067 | xtcsetpgrp(int fd, pid_t pgrp) | 4057 | xtcsetpgrp(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 | */ | ||
| 5535 | static int | ||
| 5536 | noclobberopen(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) | |||
| 5645 | static int | 5573 | static int |
| 5646 | openredirect(union node *redir) | 5574 | openredirect(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) */ | ||
| 6143 | typedef long arith_t; | ||
| 6144 | # define ARITH_FMT "%ld" | ||
| 6145 | #endif | ||
| 6146 | static int | 6085 | static int |
| 6147 | cvtnum(arith_t num) | 6086 | cvtnum(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 */ | ||
| 6573 | static int evaltree(union node *, int); | 6519 | static 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 | */ | ||
| 6526 | static ALWAYS_INLINE NORETURN void | ||
| 6527 | evaltreenr(union node *n, int flags) | ||
| 6528 | { | ||
| 6529 | evaltree(n, flags); | ||
| 6530 | bb_unreachable(abort()); | ||
| 6531 | /* NOTREACHED */ | ||
| 6532 | } | ||
| 6533 | |||
| 6575 | static void FAST_FUNC | 6534 | static void FAST_FUNC |
| 6576 | evalbackcmd(union node *n, struct backcmd *result) | 6535 | evalbackcmd(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 */ |
| 6753 | static char *evalvar(char *p, int flags, struct strlist *var_str_list); | 6712 | static 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 | */ |
| 6764 | static void | 6719 | static void |
| 6765 | argstr(char *p, int flags, struct strlist *var_str_list) | 6720 | argstr(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 | ||
| 7024 | static const char * | 6979 | static const char * |
| 7025 | subevalvar(char *p, char *varname, int strloc, int subtype, | 6980 | subevalvar(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 | */ |
| 7321 | static NOINLINE ssize_t | 7276 | static NOINLINE ssize_t |
| 7322 | varvalue(char *name, int varflags, int flags, struct strlist *var_str_list, int *quotedp) | 7277 | varvalue(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 | */ |
| 7467 | static char * | 7397 | static char * |
| 7468 | evalvar(char *p, int flag, struct strlist *var_str_list) | 7398 | evalvar(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, "ed); | 7422 | varlen = varvalue(var, varflags, flag, "ed); |
| 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 | ||
| 8146 | static void | 8069 | static void |
| 8147 | tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp) | 8070 | tryexec(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; | |||
| 8810 | static char **nodeptr; | 8734 | static 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 | |||
| 8817 | static const uint8_t nodesize[N_NUMBER] ALIGN1 = { | 8737 | static 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) | ||
| 9340 | static | ||
| 9341 | #endif | ||
| 9342 | int evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__)); | ||
| 9343 | |||
| 9344 | static int | 9259 | static int |
| 9345 | skiploop(void) | 9260 | skiploop(void) |
| 9346 | { | 9261 | { |
| @@ -9699,27 +9614,57 @@ optschanged(void) | |||
| 9699 | #endif | 9614 | #endif |
| 9700 | } | 9615 | } |
| 9701 | 9616 | ||
| 9702 | static struct localvar *localvars; | 9617 | struct localvar_list { |
| 9618 | struct localvar_list *next; | ||
| 9619 | struct localvar *lv; | ||
| 9620 | }; | ||
| 9621 | |||
| 9622 | static 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 | */ |
| 9708 | static void | 9628 | static void |
| 9709 | poplocalvars(void) | 9629 | poplocalvars(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 | */ | ||
| 9685 | static struct localvar_list * | ||
| 9686 | pushlocalvars(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 | |||
| 9700 | static void | ||
| 9701 | unwindlocalvars(struct localvar_list *stop) | ||
| 9702 | { | ||
| 9703 | while (localvar_stack != stop) | ||
| 9704 | poplocalvars(0); | ||
| 9734 | } | 9705 | } |
| 9735 | 9706 | ||
| 9736 | static int | 9707 | static int |
| 9737 | evalfun(struct funcnode *func, int argc, char **argv, int flags) | 9708 | evalfun(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 | ||
| 13021 | static inline int | ||
| 13022 | parser_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 | ||
| 13774 | static const unsigned char timescmd_str[] ALIGN1 = { | 13750 | static 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 | ||
| 2 | 127 | ||
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 | ||
| 2 | a''=b true | ||
| 3 | echo $? | ||
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 @@ | |||
| 1 | Null 0th arg: | ||
| 2 | ./empty_args.tests: line 2: : Permission denied | ||
| 3 | 127 | ||
| 4 | Null 1st arg: | ||
| 5 | 0 | ||
| 6 | Null 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 @@ | |||
| 1 | echo Null 0th arg: | ||
| 2 | "" | ||
| 3 | echo $? | ||
| 4 | echo Null 1st arg: | ||
| 5 | # printf without args would print usage info | ||
| 6 | printf "" | ||
| 7 | echo $? | ||
| 8 | echo Null arg in exec: | ||
| 9 | exec 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 @@ | |||
| 1 | var=val | ||
| 2 | var=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 @@ | |||
| 1 | var=old | ||
| 2 | f() { 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. | ||
| 7 | var=val f | ||
| 8 | echo "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 @@ | |||
| 1 | 0 | ||
| 2 | 0 | ||
| 3 | ./emptytick.tests: line 3: : Permission denied | ||
| 4 | 127 | ||
| 5 | ./emptytick.tests: line 4: : Permission denied | ||
| 6 | 127 | ||
| 7 | 0 | ||
| 8 | 0 | ||
| 9 | 0 | ||
| 10 | 0 | ||
| 11 | ./emptytick.tests: line 10: : Permission denied | ||
| 12 | 127 | ||
| 13 | ./emptytick.tests: line 11: : Permission denied | ||
| 14 | 127 | ||
| 15 | 0 | ||
| 16 | 0 | ||
| 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 @@ | |||
| 1 | true; ``; echo $? | ||
| 2 | false; ``; echo $? | ||
| 3 | true; `""`; echo $? | ||
| 4 | false; `""`; echo $? | ||
| 5 | true; ` `; echo $? | ||
| 6 | false; ` `; echo $? | ||
| 7 | |||
| 8 | true; $(); echo $? | ||
| 9 | false; $(); echo $? | ||
| 10 | true; $(""); echo $? | ||
| 11 | false; $(""); echo $? | ||
| 12 | true; $( ); echo $? | ||
| 13 | false; $( ); echo $? | ||
| 14 | |||
| 15 | exec ''; echo $? | ||
| 16 | echo 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 @@ | |||
| 1 | 1 | ||
| 2 | 1 | ||
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 @@ | |||
| 1 | true | ||
| 2 | false; echo `echo $?` | ||
| 3 | true | ||
| 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 @@ | |||
| 1 | if false; then | ||
| 2 | echo "FOO" | ||
| 3 | tmp=`echo BAR >&2` | ||
| 4 | fi | ||
| 5 | echo 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 | ||
| 3 | Q | ||
| 4 | a\bc | ||
| 5 | 11-$a-\t-\-\"-`-\--\z-\*-\?-22 33-$a-\t-\-"-`-\--\z-\*-\?-44 | ||
| 6 | done: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 @@ | |||
| 1 | test "$CONFIG_FEATURE_FANCY_ECHO" = "y" || exit 77 | ||
| 2 | |||
| 3 | TEST=Q | ||
| 4 | # \` is special | ||
| 5 | echo `echo '\'TEST\`echo ZZ\`BEST` | ||
| 6 | # \$ and \\ are special | ||
| 7 | echo `echo \\$TEST` | ||
| 8 | echo `echo \$TEST` | ||
| 9 | echo a`echo \\\\b`c | ||
| 10 | |||
| 11 | # \" is not special if in unquoted `cmd` (passed verbatim WITH \), | ||
| 12 | # but is special in quoted one | ||
| 13 | echo `echo 11'-$a-\t-\\-\"-\`-\--\z-\*-\?-'22` "`echo 33'-$a-\t-\\-\"-\`-\--\z-\*-\?-'44`" | ||
| 14 | echo 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 | ||
| 2 | TEST) BEST | ||
| 3 | ((TEST) BEST | ||
| 4 | ) | ||
| 5 | abc | ||
| 6 | a)c | ||
| 7 | OK: 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 @@ | |||
| 1 | echo $(echo '(TEST)' BEST) | ||
| 2 | echo $(echo 'TEST)' BEST) | ||
| 3 | echo $(echo \(\(TEST\) BEST) | ||
| 4 | echo $(echo \)) | ||
| 5 | echo $(echo a"`echo "b"`"c ) | ||
| 6 | echo $(echo a"`echo ")"`"c ) | ||
| 7 | echo 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 @@ | |||
| 1 | 546ed3f5c81c780d3ab86ada14824237 - | ||
| 2 | 546ed3f5c81c780d3ab86ada14824237 - | ||
| 3 | End | ||
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 | ||
| 2 | yes "123456789 123456789 123456789 123456789" | head -3000 >>"$0.tmp" | ||
| 3 | |||
| 4 | echo "`cat $0.tmp`" | md5sum | ||
| 5 | rm "$0.tmp" | ||
| 6 | yes "123456789 123456789 123456789 123456789" | head -3000 | md5sum | ||
| 7 | echo 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 @@ | |||
| 1 | sending USR2 | ||
| 2 | caught | ||
| 3 | sending USR2 | ||
| 4 | sending USR2 | ||
| 5 | User 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 | ||
| 2 | trap ":" USR1 USR2 | ||
| 3 | |||
| 4 | "$THIS_SH" -c ' | ||
| 5 | trap "echo caught" USR2 | ||
| 6 | echo "sending USR2" | ||
| 7 | kill -USR2 $$ | ||
| 8 | |||
| 9 | trap "" USR2 | ||
| 10 | echo "sending USR2" | ||
| 11 | kill -USR2 $$ | ||
| 12 | |||
| 13 | trap "-" USR2 | ||
| 14 | echo "sending USR2" | ||
| 15 | kill -USR2 $$ | ||
| 16 | |||
| 17 | echo "not reached" | ||
| 18 | ' | ||
| 19 | |||
| 20 | trap "-" 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 @@ | |||
| 1 | Hangup | ||
| 2 | Done: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 $$) & | ||
| 3 | while true; do | ||
| 4 | read ignored | ||
| 5 | done | ||
| 6 | ' | ||
| 7 | echo "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 @@ | |||
| 1 | trap -- '' HUP | ||
| 2 | trap -- '' QUIT | ||
| 3 | trap -- '' SYS | ||
| 4 | Ok | ||
| 5 | trap -- '' HUP | ||
| 6 | trap -- '' QUIT | ||
| 7 | trap -- '' SYS | ||
| 8 | Ok | ||
| 9 | trap -- '' HUP | ||
| 10 | trap -- '' QUIT | ||
| 11 | trap -- '' SYS | ||
| 12 | Ok | ||
| 13 | trap -- '' HUP | ||
| 14 | trap -- '' QUIT | ||
| 15 | trap -- '' SYS | ||
| 16 | Ok | ||
| 17 | trap -- '' HUP | ||
| 18 | trap -- '' QUIT | ||
| 19 | trap -- '' SYS | ||
| 20 | Terminated | ||
| 21 | Done | ||
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 | ||
| 4 | trap '' HUP | ||
| 5 | # QUIT is always special | ||
| 6 | trap '' QUIT | ||
| 7 | # SYS is not special | ||
| 8 | trap '' SYS | ||
| 9 | # WINCH is harmless | ||
| 10 | trap 'bad: caught WINCH' WINCH | ||
| 11 | # With TERM we'll check whether it is reset | ||
| 12 | trap '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) | ||
| 19 | echo 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 @@ | |||
| 1 | SHELL: line 1: syntax error: bad substitution | ||
| 2 | SHELL: 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 _${#+}_ | ||
| 15 | echo _${#:+}_ | ||
| 16 | # Forms with non-empty word work as expected in both ash and bash. | ||
| 17 | echo _${#+z}_ _${#:+z}_ | ||
| 18 | |||
| 19 | # now some valid ones | ||
| 20 | set -- | ||
| 21 | echo _$1 _${1+} _${1:+} _${1+word} _${1:+word} | ||
| 22 | |||
| 23 | set -- aaaa | ||
| 24 | echo _$1 _${1+} _${1:+} _${1+word} _${1:+word} | ||
| 25 | |||
| 26 | unset f | ||
| 27 | echo _$f _${f+} _${f:+} _${f+word} _${f:+word} | ||
| 28 | |||
| 29 | f= | ||
| 30 | echo _$f _${f+} _${f:+} _${f+word} _${f:+word} | ||
| 31 | |||
| 32 | f=fff | ||
| 33 | echo _$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 @@ | |||
| 1 | SHELL: line 1: syntax error: bad substitution | ||
| 2 | SHELL: line 1: syntax error: bad substitution | ||
| 3 | 0 | ||
| 4 | 0 | ||
| 5 | SHELL: line 1: 1: bad variable name | ||
| 6 | SHELL: line 1: 1: bad variable name | ||
| 7 | SHELL: line 1: 1: bad variable name | ||
| 8 | SHELL: 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 | ||
| 23 | unset f; echo _$f | ||
| 24 | unset f; echo _${f=} | ||
| 25 | unset f; echo _${f:=} | ||
| 26 | unset f; echo _${f=word} | ||
| 27 | unset f; echo _${f:=word} | ||
| 28 | |||
| 29 | f=; echo _$f | ||
| 30 | f=; echo _${f=} | ||
| 31 | f=; echo _${f:=} | ||
| 32 | f=; echo _${f=word} | ||
| 33 | f=; echo _${f:=word} | ||
| 34 | |||
| 35 | f=fff; echo _$f | ||
| 36 | f=fff; echo _${f=} | ||
| 37 | f=fff; echo _${f:=} | ||
| 38 | f=fff; echo _${f=word} | ||
| 39 | f=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 @@ | |||
| 1 | SHELL: line 1: syntax error: bad substitution | ||
| 2 | SHELL: line 1: syntax error: bad substitution | ||
| 3 | SHELL: line 1: syntax error: bad substitution | ||
| 4 | SHELL: line 1: syntax error: bad substitution | ||
| 5 | SHELL: line 1: syntax error: missing '}' | ||
| 6 | 1 =|| | ||
| 7 | 1:1 =|| | ||
| 8 | 1:1:2=|| | ||
| 9 | 1::2 =|| | ||
| 10 | 1:1: =|| | ||
| 11 | 1:: =|| | ||
| 12 | 1 =|0123| | ||
| 13 | 1:1 =|123| | ||
| 14 | 1:1:2=|12| | ||
| 15 | 1::2 =|01| | ||
| 16 | 1:1: =|| | ||
| 17 | 1:: =|| | ||
| 18 | f =|| | ||
| 19 | f:1 =|| | ||
| 20 | f:1:2=|| | ||
| 21 | f::2 =|| | ||
| 22 | f:1: =|| | ||
| 23 | f:: =|| | ||
| 24 | f =|| | ||
| 25 | f:1 =|| | ||
| 26 | f:1:2=|| | ||
| 27 | f::2 =|| | ||
| 28 | f:1: =|| | ||
| 29 | f:: =|| | ||
| 30 | f =|a| | ||
| 31 | f:1 =|| | ||
| 32 | f:1:2=|| | ||
| 33 | f::2 =|a| | ||
| 34 | f:1: =|| | ||
| 35 | f:: =|| | ||
| 36 | f =|0123456789| | ||
| 37 | f:1 =|123456789| | ||
| 38 | f:1:2=|12| | ||
| 39 | f::2 =|01| | ||
| 40 | f:1: =|| | ||
| 41 | f:: =|| | ||
| 42 | Substrings 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 | #:: =|| | ||
| 55 | Substrings with expressions | ||
| 56 | f =|01234567| | ||
| 57 | f:1+1:2+2 =|2345| | ||
| 58 | f:-1:2+2 =|01234567| | ||
| 59 | f:1:f =|1234567| | ||
| 60 | f:1:$f =|1234567| | ||
| 61 | f:1:${f} =|1234567| | ||
| 62 | f:1:${f:3:1} =|123| | ||
| 63 | f:1:1`echo 1`=|1| | ||
| 64 | Done | ||
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) | ||
| 4 | export 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 | ||
| 17 | set --; echo "1 =|${1}|" | ||
| 18 | set --; echo "1:1 =|${1:1}|" | ||
| 19 | set --; echo "1:1:2=|${1:1:2}|" | ||
| 20 | set --; echo "1::2 =|${1::2}|" | ||
| 21 | set --; echo "1:1: =|${1:1:}|" | ||
| 22 | set --; echo "1:: =|${1::}|" | ||
| 23 | |||
| 24 | set -- 0123; echo "1 =|${1}|" | ||
| 25 | set -- 0123; echo "1:1 =|${1:1}|" | ||
| 26 | set -- 0123; echo "1:1:2=|${1:1:2}|" | ||
| 27 | set -- 0123; echo "1::2 =|${1::2}|" | ||
| 28 | set -- 0123; echo "1:1: =|${1:1:}|" | ||
| 29 | set -- 0123; echo "1:: =|${1::}|" | ||
| 30 | |||
| 31 | unset f; echo "f =|$f|" | ||
| 32 | unset f; echo "f:1 =|${f:1}|" | ||
| 33 | unset f; echo "f:1:2=|${f:1:2}|" | ||
| 34 | unset f; echo "f::2 =|${f::2}|" | ||
| 35 | unset f; echo "f:1: =|${f:1:}|" | ||
| 36 | unset f; echo "f:: =|${f::}|" | ||
| 37 | |||
| 38 | f=; echo "f =|$f|" | ||
| 39 | f=; echo "f:1 =|${f:1}|" | ||
| 40 | f=; echo "f:1:2=|${f:1:2}|" | ||
| 41 | f=; echo "f::2 =|${f::2}|" | ||
| 42 | f=; echo "f:1: =|${f:1:}|" | ||
| 43 | f=; echo "f:: =|${f::}|" | ||
| 44 | |||
| 45 | f=a; echo "f =|$f|" | ||
| 46 | f=a; echo "f:1 =|${f:1}|" | ||
| 47 | f=a; echo "f:1:2=|${f:1:2}|" | ||
| 48 | f=a; echo "f::2 =|${f::2}|" | ||
| 49 | f=a; echo "f:1: =|${f:1:}|" | ||
| 50 | f=a; echo "f:: =|${f::}|" | ||
| 51 | |||
| 52 | f=0123456789; echo "f =|$f|" | ||
| 53 | f=0123456789; echo "f:1 =|${f:1}|" | ||
| 54 | f=0123456789; echo "f:1:2=|${f:1:2}|" | ||
| 55 | f=0123456789; echo "f::2 =|${f::2}|" | ||
| 56 | f=0123456789; echo "f:1: =|${f:1:}|" | ||
| 57 | f=0123456789; echo "f:: =|${f::}|" | ||
| 58 | |||
| 59 | echo "Substrings from special vars" | ||
| 60 | echo '? '"=|$?|" | ||
| 61 | echo '?:1 '"=|${?:1}|" | ||
| 62 | echo '?:1:2'"=|${?:1:2}|" | ||
| 63 | echo '?::2 '"=|${?::2}|" | ||
| 64 | echo '?:1: '"=|${?:1:}|" | ||
| 65 | echo '?:: '"=|${?::}|" | ||
| 66 | set -- 1 2 3 4 5 6 7 8 9 10 11 | ||
| 67 | echo '# '"=|$#|" | ||
| 68 | echo '#:1 '"=|${#:1}|" | ||
| 69 | echo '#:1:2'"=|${#:1:2}|" | ||
| 70 | echo '#::2 '"=|${#::2}|" | ||
| 71 | echo '#:1: '"=|${#:1:}|" | ||
| 72 | echo '#:: '"=|${#::}|" | ||
| 73 | |||
| 74 | echo "Substrings with expressions" | ||
| 75 | f=01234567; echo 'f '"=|$f|" | ||
| 76 | f=01234567; echo 'f:1+1:2+2 '"=|${f:1+1:2+2}|" | ||
| 77 | f=01234567; echo 'f:-1:2+2 '"=|${f:-1:2+2}|" | ||
| 78 | f=01234567; echo 'f:1:f '"=|${f:1:f}|" | ||
| 79 | f=01234567; echo 'f:1:$f '"=|${f:1:$f}|" | ||
| 80 | f=01234567; echo 'f:1:${f} '"=|${f:1:${f}}|" | ||
| 81 | f=01234567; echo 'f:1:${f:3:1} '"=|${f:1:${f:3:1}}|" | ||
| 82 | f=01234567; echo 'f:1:1`echo 1`'"=|${f:1:`echo 1`}|" | ||
| 83 | |||
| 84 | echo 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 @@ | |||
| 1 | SHELL: 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 | ||
| 7 | echo _${#-} _${#:-} | ||
| 8 | |||
| 9 | # now some valid ones | ||
| 10 | set -- | ||
| 11 | echo _$1 _${1-} _${1:-} _${1-word} _${1:-word} | ||
| 12 | |||
| 13 | set -- aaaa | ||
| 14 | echo _$1 _${1-} _${1:-} _${1-word} _${1:-word} | ||
| 15 | |||
| 16 | unset f | ||
| 17 | echo _$f _${f-} _${f:-} _${f-word} _${f:-word} | ||
| 18 | |||
| 19 | f= | ||
| 20 | echo _$f _${f-} _${f:-} _${f-word} _${f:-word} | ||
| 21 | |||
| 22 | f=fff | ||
| 23 | echo _$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 @@ | |||
| 1 | SHELL: line 1: syntax error: bad substitution | ||
| 2 | 1 | ||
| 3 | 0 | ||
| 4 | ==== | ||
| 5 | _ | ||
| 6 | SHELL: line 1: 1: parameter not set | ||
| 7 | SHELL: line 1: 1: parameter not set or null | ||
| 8 | SHELL: line 1: 1: message1 | ||
| 9 | SHELL: line 1: 1: message1 | ||
| 10 | SHELL: line 1: 1: unset! | ||
| 11 | SHELL: line 1: 1: null or unset! | ||
| 12 | ==== | ||
| 13 | _aaaa | ||
| 14 | _aaaa | ||
| 15 | _aaaa | ||
| 16 | _aaaa | ||
| 17 | _aaaa | ||
| 18 | _aaaa | ||
| 19 | _aaaa | ||
| 20 | ==== | ||
| 21 | _ | ||
| 22 | SHELL: line 1: f: parameter not set | ||
| 23 | SHELL: line 1: f: parameter not set or null | ||
| 24 | SHELL: line 1: f: message3 | ||
| 25 | SHELL: line 1: f: message3 | ||
| 26 | SHELL: line 1: f: unset! | ||
| 27 | SHELL: line 1: f: null or unset! | ||
| 28 | ==== | ||
| 29 | _ | ||
| 30 | _ | ||
| 31 | SHELL: line 1: f: parameter not set or null | ||
| 32 | _ | ||
| 33 | SHELL: line 1: f: message4 | ||
| 34 | _ | ||
| 35 | SHELL: 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 | ||
| 15 | export msg_unset="unset!" | ||
| 16 | export msg_null_or_unset="null or unset!" | ||
| 17 | |||
| 18 | echo ==== | ||
| 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 | |||
| 27 | echo ==== | ||
| 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 | |||
| 36 | echo ==== | ||
| 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 | |||
| 45 | echo ==== | ||
| 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 | |||
| 54 | echo ==== | ||
| 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 @@ | |||
| 1 | One:1 | ||
| 2 | Two:2 | ||
| 3 | Three:3 | ||
| 4 | |||
| 5 | One:1 | ||
| 6 | Two:2 | ||
| 7 | Three:3 | ||
| 8 | |||
| 9 | Ok ${#$}: 0 | ||
| 10 | |||
| 11 | Ok ${#!}: 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 | |||
| 3 | false | ||
| 4 | echo One:${#?} | ||
| 5 | (exit 10) | ||
| 6 | echo Two:${#?} | ||
| 7 | (exit 100) | ||
| 8 | echo Three:${#?} | ||
| 9 | |||
| 10 | echo | ||
| 11 | echo One:${##} | ||
| 12 | set -- 1 2 3 4 5 6 7 8 9 0 | ||
| 13 | echo Two:${##} | ||
| 14 | set -- 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 | ||
| 18 | echo Three:${##} | ||
| 19 | |||
| 20 | echo | ||
| 21 | v=$$ | ||
| 22 | test "${#v}" = "${#$}" | ||
| 23 | echo 'Ok ${#$}:' $? | ||
| 24 | |||
| 25 | echo | ||
| 26 | sleep 0 & | ||
| 27 | v=$! | ||
| 28 | test "${#v}" = "${#!}" | ||
| 29 | echo '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 |
| 11 | Fail:2 | 11 | Fail:2 |
| 12 | 12 | ||
| 13 | Fail: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 | ||
| 2 | 2 | ||
| 3 | ./unset.tests: unset: line 5: illegal option -m | ||
| 4 | 2 | ||
| 5 | 0 | ||
| 6 | ___ | ||
| 7 | 0 f g | ||
| 8 | 0 g | ||
| 9 | 0 | ||
| 10 | ___ | ||
| 11 | 0 f g | ||
| 12 | 0 | ||
| 13 | 0 f g | ||
| 14 | 0 | ||
| 15 | ___ | ||
| 16 | ./unset.tests: unset: line 36: VAR_RO: is read only | ||
| 17 | 2 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 -) | ||
| 4 | echo $? | ||
| 5 | (unset -m a b c) | ||
| 6 | echo $? | ||
| 7 | |||
| 8 | # check funky usage | ||
| 9 | unset | ||
| 10 | echo $? | ||
| 11 | |||
| 12 | # check normal usage | ||
| 13 | echo ___ | ||
| 14 | f=f g=g | ||
| 15 | echo $? $f $g | ||
| 16 | unset f | ||
| 17 | echo $? $f $g | ||
| 18 | unset g | ||
| 19 | echo $? $f $g | ||
| 20 | |||
| 21 | echo ___ | ||
| 22 | f=f g=g | ||
| 23 | echo $? $f $g | ||
| 24 | unset f g | ||
| 25 | echo $? $f $g | ||
| 26 | f=f g=g | ||
| 27 | echo $? $f $g | ||
| 28 | unset -v f g | ||
| 29 | echo $? $f $g | ||
| 30 | |||
| 31 | # check read only vars | ||
| 32 | echo ___ | ||
| 33 | f=f g=g | ||
| 34 | VAR_RO=1 | ||
| 35 | readonly VAR_RO | ||
| 36 | (unset VAR_RO) | ||
| 37 | echo $? $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 | ||
| 6665 | static 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 | |||
| 6646 | static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | 6674 | static 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 @@ | |||
| 1 | var=old | 1 | var=old |
| 2 | f() { echo "var=$var"; } | 2 | f() { 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. | ||
| 3 | var=val f | 7 | var=val f |
| 4 | echo "var=$var" | 8 | echo "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 @@ | |||
| 1 | hush: write error: Bad file descriptor | ||
| 2 | TEST | ||
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 | ||
| 2 | exec 1>&- | ||
| 3 | echo TEST >TEST | ||
| 4 | echo JUNK # lost: stdout is closed | ||
| 5 | cat TEST >&2 | ||
| 6 | rm 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 @@ | |||
| 1 | hush: syntax error: unterminated ${name} | 1 | hush: syntax error: unterminated ${name} |
| 2 | hush: syntax error: unterminated ${name} | 2 | hush: 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. |
| 6 | echo _${#+}_ _${#:+}_ | 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 _${#+}_ | ||
| 15 | echo _${#:+}_ | ||
| 16 | # Forms with non-empty word work as expected in both ash and bash. | ||
| 17 | echo _${#+z}_ _${#:+z}_ | ||
| 7 | 18 | ||
| 8 | # now some valid ones | 19 | # now some valid ones |
| 9 | set -- | 20 | set -- |
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 |
| 22 | unset f; echo _$f | 23 | unset 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) | ||
| 3 | export var=0123456789 | 4 | export 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 |
| 6 | echo _${#-} _${#:-} | 8 | echo _${#-} _${#:-} |
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 @@ | |||
| 1 | hush: syntax error: unterminated ${name} | 1 | hush: syntax error: unterminated ${name} |
| 2 | 0 | 2 | 1 |
| 3 | 0 | 3 | 0 |
| 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 @@ | |||
| 1 | One:1 | ||
| 2 | Two:2 | ||
| 3 | Three:3 | ||
| 4 | |||
| 5 | One:1 | ||
| 6 | Two:2 | ||
| 7 | Three:3 | ||
| 8 | |||
| 9 | Ok ${#$}: 0 | ||
| 10 | |||
| 11 | Ok ${#!}: 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 | |||
| 3 | false | ||
| 4 | echo One:${#?} | ||
| 5 | (exit 10) | ||
| 6 | echo Two:${#?} | ||
| 7 | (exit 100) | ||
| 8 | echo Three:${#?} | ||
| 9 | |||
| 10 | echo | ||
| 11 | echo One:${##} | ||
| 12 | set -- 1 2 3 4 5 6 7 8 9 0 | ||
| 13 | echo Two:${##} | ||
| 14 | set -- 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 | ||
| 18 | echo Three:${##} | ||
| 19 | |||
| 20 | echo | ||
| 21 | v=$$ | ||
| 22 | test "${#v}" = "${#$}" | ||
| 23 | echo 'Ok ${#$}:' $? | ||
| 24 | |||
| 25 | echo | ||
| 26 | sleep 0 & | ||
| 27 | v=$! | ||
| 28 | test "${#v}" = "${#!}" | ||
| 29 | echo '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 @@ ___ | |||
| 12 | 0 f g | 12 | 0 f g |
| 13 | 0 | 13 | 0 |
| 14 | ___ | 14 | ___ |
| 15 | hush: HUSH_VERSION: readonly variable | 15 | hush: VAR_RO: readonly variable |
| 16 | 1 f g | 16 | 1 f g |
| 17 | hush: HUSH_VERSION: readonly variable | 17 | hush: VAR_RO: readonly variable |
| 18 | 1 | 18 | 1 |
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 | ||
| 2 | unset - | 3 | unset - |
| 3 | echo $? | 4 | echo $? |
| 4 | unset -m a b c | 5 | unset -m a b c |
| @@ -30,7 +31,9 @@ echo $? $f $g | |||
| 30 | # check read only vars | 31 | # check read only vars |
| 31 | echo ___ | 32 | echo ___ |
| 32 | f=f g=g | 33 | f=f g=g |
| 33 | unset HUSH_VERSION | 34 | VAR_RO=1 |
| 35 | readonly VAR_RO | ||
| 36 | unset VAR_RO | ||
| 34 | echo $? $f $g | 37 | echo $? $f $g |
| 35 | unset f HUSH_VERSION g | 38 | unset f VAR_RO g |
| 36 | echo $? $f $g | 39 | echo $? $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 | |||
| 10 | unset LC_ALL | 10 | unset LC_ALL |
| 11 | umask 022 | 11 | umask 022 |
| 12 | 12 | ||
| 13 | rm -rf tar.tempdir 2>/dev/null | ||
| 14 | mkdir 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 | ||
| 18 | testing "Empty file is not a tarball" '\ | 15 | testing "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 | "" "" |
| 54 | SKIP= | 51 | SKIP= |
| 55 | 52 | ||
| 53 | mkdir 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. |
| 65 | optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES | 63 | optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES |
| 66 | testing "tar hardlinks and repeated files" '\ | 64 | testing "tar hardlinks and repeated files" '\ |
| 67 | rm -rf input_* test.tar 2>/dev/null | ||
| 68 | >input_hard1 | 65 | >input_hard1 |
| 69 | ln input_hard1 input_hard2 | 66 | ln input_hard1 input_hard2 |
| 70 | mkdir input_dir | 67 | mkdir input_dir |
| @@ -95,10 +92,11 @@ drwxr-xr-x input_dir | |||
| 95 | " \ | 92 | " \ |
| 96 | "" "" | 93 | "" "" |
| 97 | SKIP= | 94 | SKIP= |
| 95 | cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null | ||
| 98 | 96 | ||
| 97 | mkdir tar.tempdir && cd tar.tempdir || exit 1 | ||
| 99 | optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES | 98 | optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES |
| 100 | testing "tar hardlinks mode" '\ | 99 | testing "tar hardlinks mode" '\ |
| 101 | rm -rf input_* test.tar 2>/dev/null | ||
| 102 | >input_hard1 | 100 | >input_hard1 |
| 103 | chmod 741 input_hard1 | 101 | chmod 741 input_hard1 |
| 104 | ln input_hard1 input_hard2 | 102 | ln input_hard1 input_hard2 |
| @@ -128,10 +126,11 @@ Ok: 0 | |||
| 128 | " \ | 126 | " \ |
| 129 | "" "" | 127 | "" "" |
| 130 | SKIP= | 128 | SKIP= |
| 129 | cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null | ||
| 131 | 130 | ||
| 131 | mkdir tar.tempdir && cd tar.tempdir || exit 1 | ||
| 132 | optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES | 132 | optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES |
| 133 | testing "tar symlinks mode" '\ | 133 | testing "tar symlinks mode" '\ |
| 134 | rm -rf input_* test.tar 2>/dev/null | ||
| 135 | >input_file | 134 | >input_file |
| 136 | chmod 741 input_file | 135 | chmod 741 input_file |
| 137 | ln -s input_file input_soft | 136 | ln -s input_file input_soft |
| @@ -159,10 +158,11 @@ lrwxrwxrwx input_file | |||
| 159 | " \ | 158 | " \ |
| 160 | "" "" | 159 | "" "" |
| 161 | SKIP= | 160 | SKIP= |
| 161 | cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null | ||
| 162 | 162 | ||
| 163 | mkdir tar.tempdir && cd tar.tempdir || exit 1 | ||
| 163 | optional FEATURE_TAR_CREATE FEATURE_TAR_LONG_OPTIONS | 164 | optional FEATURE_TAR_CREATE FEATURE_TAR_LONG_OPTIONS |
| 164 | testing "tar --overwrite" "\ | 165 | testing "tar --overwrite" "\ |
| 165 | rm -rf input_* test.tar 2>/dev/null | ||
| 166 | ln input input_hard | 166 | ln input input_hard |
| 167 | tar cf test.tar input_hard | 167 | tar cf test.tar input_hard |
| 168 | echo WRONG >input | 168 | echo WRONG >input |
| @@ -174,12 +174,13 @@ Ok | |||
| 174 | " \ | 174 | " \ |
| 175 | "Ok\n" "" | 175 | "Ok\n" "" |
| 176 | SKIP= | 176 | SKIP= |
| 177 | cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null | ||
| 177 | 178 | ||
| 179 | mkdir tar.tempdir && cd tar.tempdir || exit 1 | ||
| 178 | test x"$SKIP_KNOWN_BUGS" = x"" && { | 180 | test 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 |
| 180 | optional FEATURE_TAR_CREATE | 182 | optional FEATURE_TAR_CREATE |
| 181 | testing "tar writing into read-only dir" '\ | 183 | testing "tar writing into read-only dir" '\ |
| 182 | rm -rf input_* test.tar 2>/dev/null | ||
| 183 | mkdir input_dir | 184 | mkdir input_dir |
| 184 | >input_dir/input_file | 185 | >input_dir/input_file |
| 185 | chmod 550 input_dir | 186 | chmod 550 input_dir |
| @@ -201,7 +202,9 @@ dr-xr-x--- input_dir | |||
| 201 | "" "" | 202 | "" "" |
| 202 | SKIP= | 203 | SKIP= |
| 203 | } | 204 | } |
| 205 | cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null | ||
| 204 | 206 | ||
| 207 | mkdir 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 |
| 207 | optional FEATURE_TAR_CREATE FEATURE_SEAMLESS_GZ GUNZIP | 210 | optional FEATURE_TAR_CREATE FEATURE_SEAMLESS_GZ GUNZIP |
| @@ -217,7 +220,9 @@ Ok | |||
| 217 | " \ | 220 | " \ |
| 218 | "" "" | 221 | "" "" |
| 219 | SKIP= | 222 | SKIP= |
| 223 | cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null | ||
| 220 | 224 | ||
| 225 | mkdir 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") |
| 223 | optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_XZ | 228 | optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_XZ |
| @@ -236,7 +241,9 @@ AAAEWVo= | |||
| 236 | ==== | 241 | ==== |
| 237 | " | 242 | " |
| 238 | SKIP= | 243 | SKIP= |
| 244 | cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null | ||
| 239 | 245 | ||
| 246 | mkdir 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 |
| 241 | optional FEATURE_TAR_CREATE | 248 | optional FEATURE_TAR_CREATE |
| 242 | testing "tar strips /../ on extract" "\ | 249 | testing "tar strips /../ on extract" "\ |
| @@ -255,7 +262,9 @@ Ok | |||
| 255 | " \ | 262 | " \ |
| 256 | "" "" | 263 | "" "" |
| 257 | SKIP= | 264 | SKIP= |
| 265 | cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null | ||
| 258 | 266 | ||
| 267 | mkdir 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 | |||
| 270 | testing "tar does not extract into symlinks" "\ | 279 | testing "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 | " "\ |
| 282 | tar: can't create symlink 'passwd' to '/tmp/passwd' | ||
| 273 | 0 | 283 | 0 |
| 274 | " \ | 284 | " \ |
| 275 | "" "\ | 285 | "" "\ |
| @@ -281,12 +291,15 @@ l4/V8LDoe90yiWJhOJvIypgEfxdyRThQkBVn/bI= | |||
| 281 | ==== | 291 | ==== |
| 282 | " | 292 | " |
| 283 | SKIP= | 293 | SKIP= |
| 294 | cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null | ||
| 295 | |||
| 296 | mkdir tar.tempdir && cd tar.tempdir || exit 1 | ||
| 284 | # And same with -k | 297 | # And same with -k |
| 285 | optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2 | 298 | optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2 |
| 286 | testing "tar -k does not extract into symlinks" "\ | 299 | testing "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 | " "\ |
| 289 | tar: can't open 'passwd': File exists | 302 | tar: can't create symlink 'passwd' to '/tmp/passwd' |
| 290 | 0 | 303 | 0 |
| 291 | " \ | 304 | " \ |
| 292 | "" "\ | 305 | "" "\ |
| @@ -298,7 +311,9 @@ l4/V8LDoe90yiWJhOJvIypgEfxdyRThQkBVn/bI= | |||
| 298 | ==== | 311 | ==== |
| 299 | " | 312 | " |
| 300 | SKIP= | 313 | SKIP= |
| 314 | cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null | ||
| 301 | 315 | ||
| 316 | mkdir tar.tempdir && cd tar.tempdir || exit 1 | ||
| 302 | optional UNICODE_SUPPORT FEATURE_TAR_GNU_EXTENSIONS FEATURE_SEAMLESS_BZ2 FEATURE_TAR_AUTODETECT | 317 | optional UNICODE_SUPPORT FEATURE_TAR_GNU_EXTENSIONS FEATURE_SEAMLESS_BZ2 FEATURE_TAR_AUTODETECT |
| 303 | testing "Pax-encoded UTF8 names and symlinks" '\ | 318 | testing "Pax-encoded UTF8 names and symlinks" '\ |
| 304 | tar xvf ../tar.utf8.tar.bz2 2>&1; echo $? | 319 | tar 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 | "" "" |
| 320 | SKIP= | 335 | SKIP= |
| 336 | cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null | ||
| 321 | 337 | ||
| 322 | 338 | mkdir tar.tempdir && cd tar.tempdir || exit 1 | |
| 323 | cd .. && rm -rf tar.tempdir || exit 1 | 339 | optional FEATURE_SEAMLESS_BZ2 FEATURE_TAR_AUTODETECT |
| 340 | testing "Symlink attack: create symlink and then write through it" '\ | ||
| 341 | exec 2>&1 | ||
| 342 | uudecode -o input && tar xvf input; echo $? | ||
| 343 | ls /tmp/bb_test_evilfile | ||
| 344 | ls bb_test_evilfile | ||
| 345 | ls symlink/bb_test_evilfile | ||
| 346 | ' "\ | ||
| 347 | anything.txt | ||
| 348 | symlink | ||
| 349 | symlink/bb_test_evilfile | ||
| 350 | tar: can't create symlink 'symlink' to '/tmp' | ||
| 351 | 1 | ||
| 352 | ls: /tmp/bb_test_evilfile: No such file or directory | ||
| 353 | ls: bb_test_evilfile: No such file or directory | ||
| 354 | symlink/bb_test_evilfile | ||
| 355 | " \ | ||
| 356 | "" "\ | ||
| 357 | begin-base64 644 tar_symlink_attack.tar.bz2 | ||
| 358 | QlpoOTFBWSZTWZgs7bQAALT/hMmQAFBAAf+AEMAGJPPv32AAAIAIMAC5thlR | ||
| 359 | omAjAmCMADQT1BqNE0AEwAAjAEwElTKeo9NTR6h6gaeoA0DQNLVdwZZ5iNTk | ||
| 360 | AQwCAV6S00QFJYhrlfFkVCEDEGtgNVqYrI0uK3ggnt30gqk4e1TTQm5QIAKa | ||
| 361 | SJqzRGSFLMmOloHSAcvLiFxxRiQtQZF+qPxbo173ZDISOAoNoPN4PQPhBhKS | ||
| 362 | n8fYaKlioCTzL2oXYczyUUIP4u5IpwoSEwWdtoA= | ||
| 363 | ==== | ||
| 364 | " | ||
| 365 | SKIP= | ||
| 366 | cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null | ||
| 324 | 367 | ||
| 325 | exit $FAILCOUNT | 368 | exit $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 */ |
| 43 | struct 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 | ||
| 45 | int FAST_FUNC volume_id_probe_minix(struct volume_id *id, uint64_t off) | 61 | int 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*/); | 196 | int 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 | ||
