diff options
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 | ||