From 621fc50e83f7446a060f0b9689dc8dc59ee0743a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 24 Jul 2017 12:42:17 +0200 Subject: hush: fix a case when redirect to a closed fd #1 is not restoring (closing) it function old new delta setup_redirects 200 245 +45 append_squirrel - 41 +41 save_fds_on_redirect 256 221 -35 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 1/1 up/down: 86/-35) Total: 51 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 34 +++++++++++++++++++++++++++------- shell/hush_test/hush-redir/redir.right | 2 ++ shell/hush_test/hush-redir/redir.tests | 6 ++++++ 3 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 shell/hush_test/hush-redir/redir.right create mode 100755 shell/hush_test/hush-redir/redir.tests diff --git a/shell/hush.c b/shell/hush.c index 309ed2139..20b092398 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -6643,8 +6643,18 @@ struct squirrel { /* moved_to = -1: fd was opened by redirect; close orig_fd after redir */ }; +static struct squirrel *append_squirrel(struct squirrel *sq, int i, int orig, int moved) +{ + sq = xrealloc(sq, (i + 2) * sizeof(sq[0])); + sq[i].orig_fd = orig; + sq[i].moved_to = moved; + sq[i+1].orig_fd = -1; /* end marker */ + return sq; +} + static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) { + int moved_to; int i = 0; if (sq) while (sq[i].orig_fd >= 0) { @@ -6664,15 +6674,12 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) i++; } - sq = xrealloc(sq, (i + 2) * sizeof(sq[0])); - sq[i].orig_fd = fd; /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */ - sq[i].moved_to = fcntl_F_DUPFD(fd, avoid_fd); - debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, sq[i].moved_to); - if (sq[i].moved_to < 0 && errno != EBADF) + moved_to = fcntl_F_DUPFD(fd, avoid_fd); + debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to); + if (moved_to < 0 && errno != EBADF) xfunc_die(); - sq[i+1].orig_fd = -1; /* end marker */ - return sq; + return append_squirrel(sq, i, fd, moved_to); } /* fd: redirect wants this fd to be used (e.g. 3>file). @@ -6778,6 +6785,19 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) */ return 1; } + if (openfd == redir->rd_fd && sqp) { + /* open() gave us precisely the fd we wanted. + * This means that this fd was not busy + * (not opened to anywhere). + * Remember to close it on restore: + */ + struct squirrel *sq = *sqp; + int i = 0; + if (sq) while (sq[i].orig_fd >= 0) + i++; + *sqp = append_squirrel(sq, i, openfd, -1); /* -1 = "it was closed" */ + debug_printf_redir("redir to previously closed fd %d\n", openfd); + } } else { /* "rd_fd<*>rd_dup" or "rd_fd<*>-" cases */ openfd = redir->rd_dup; 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 @@ +hush: write error: Bad file descriptor +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 @@ +# test: closed fds should stay closed +exec 1>&- +echo TEST >TEST +echo JUNK # lost: stdout is closed +cat TEST >&2 +rm TEST -- cgit v1.2.3-55-g6feb From 4a1d8f6d6e4e1cafc4c2b611126401bf85812a64 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 24 Jul 2017 12:49:49 +0200 Subject: ash: add most of hush process subst tests ash passes these. Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-psubst/emptytick.right | 17 +++++++++++++++++ shell/ash_test/ash-psubst/emptytick.tests | 16 ++++++++++++++++ shell/ash_test/ash-psubst/tick.right | 2 ++ shell/ash_test/ash-psubst/tick.tests | 4 ++++ shell/ash_test/ash-psubst/tick2.right | 1 + shell/ash_test/ash-psubst/tick2.tests | 5 +++++ shell/ash_test/ash-psubst/tick3.right | 6 ++++++ shell/ash_test/ash-psubst/tick3.tests | 14 ++++++++++++++ shell/ash_test/ash-psubst/tick4.right | 7 +++++++ shell/ash_test/ash-psubst/tick4.tests | 7 +++++++ shell/ash_test/ash-psubst/tick_huge.right | 3 +++ shell/ash_test/ash-psubst/tick_huge.tests | 7 +++++++ 12 files changed, 89 insertions(+) create mode 100644 shell/ash_test/ash-psubst/emptytick.right create mode 100755 shell/ash_test/ash-psubst/emptytick.tests create mode 100644 shell/ash_test/ash-psubst/tick.right create mode 100755 shell/ash_test/ash-psubst/tick.tests create mode 100644 shell/ash_test/ash-psubst/tick2.right create mode 100755 shell/ash_test/ash-psubst/tick2.tests create mode 100644 shell/ash_test/ash-psubst/tick3.right create mode 100755 shell/ash_test/ash-psubst/tick3.tests create mode 100644 shell/ash_test/ash-psubst/tick4.right create mode 100755 shell/ash_test/ash-psubst/tick4.tests create mode 100644 shell/ash_test/ash-psubst/tick_huge.right create mode 100755 shell/ash_test/ash-psubst/tick_huge.tests 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 @@ +0 +0 +./emptytick.tests: line 3: : Permission denied +127 +./emptytick.tests: line 4: : Permission denied +127 +0 +0 +0 +0 +./emptytick.tests: line 10: : Permission denied +127 +./emptytick.tests: line 11: : Permission denied +127 +0 +0 +./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 @@ +true; ``; echo $? +false; ``; echo $? +true; `""`; echo $? +false; `""`; echo $? +true; ` `; echo $? +false; ` `; echo $? + +true; $(); echo $? +false; $(); echo $? +true; $(""); echo $? +false; $(""); echo $? +true; $( ); echo $? +false; $( ); echo $? + +exec ''; echo $? +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 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 @@ +true +false; echo `echo $?` +true +{ 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 @@ +if false; then + echo "FOO" + tmp=`echo BAR >&2` +fi +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 @@ +\TESTZZBEST +$TEST +Q +a\bc +11-$a-\t-\-\"-`-\--\z-\*-\?-22 33-$a-\t-\-"-`-\--\z-\*-\?-44 +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 @@ +test "$CONFIG_FEATURE_FANCY_ECHO" = "y" || exit 77 + +TEST=Q +# \` is special +echo `echo '\'TEST\`echo ZZ\`BEST` +# \$ and \\ are special +echo `echo \\$TEST` +echo `echo \$TEST` +echo a`echo \\\\b`c + +# \" is not special if in unquoted `cmd` (passed verbatim WITH \), +# but is special in quoted one +echo `echo 11'-$a-\t-\\-\"-\`-\--\z-\*-\?-'22` "`echo 33'-$a-\t-\\-\"-\`-\--\z-\*-\?-'44`" +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 @@ +(TEST) BEST +TEST) BEST +((TEST) BEST +) +abc +a)c +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 @@ +echo $(echo '(TEST)' BEST) +echo $(echo 'TEST)' BEST) +echo $(echo \(\(TEST\) BEST) +echo $(echo \)) +echo $(echo a"`echo "b"`"c ) +echo $(echo a"`echo ")"`"c ) +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 @@ +546ed3f5c81c780d3ab86ada14824237 - +546ed3f5c81c780d3ab86ada14824237 - +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 @@ +# This creates 120k file +yes "123456789 123456789 123456789 123456789" | head -3000 >>"$0.tmp" + +echo "`cat $0.tmp`" | md5sum +rm "$0.tmp" +yes "123456789 123456789 123456789 123456789" | head -3000 | md5sum +echo End -- cgit v1.2.3-55-g6feb From c810978552bc0133ba723ababaa178c8d53256e1 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 24 Jul 2017 13:37:38 +0200 Subject: udhcpc: downgrade "MAC X:X:X:X:X:X" message to log2 level Signed-off-by: Denys Vlasenko --- networking/udhcp/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 return -1; } memcpy(mac, ifr->ifr_hwaddr.sa_data, 6); - log1("MAC %02x:%02x:%02x:%02x:%02x:%02x", + log2("MAC %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } -- cgit v1.2.3-55-g6feb From b920a38dc0a87f5884444d4731a8b887b5e16018 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 24 Jul 2017 17:20:13 +0200 Subject: tar: postpone creation of symlinks with "suspicious" targets. Closes 8411 function old new delta data_extract_all 968 1038 +70 tar_main 952 986 +34 scan_tree 258 262 +4 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/0 up/down: 108/0) Total: 108 bytes Signed-off-by: Denys Vlasenko --- archival/libarchive/data_extract_all.c | 42 ++++++++++++++++++---- archival/tar.c | 37 +++++++++---------- archival/tar_symlink_attack | 16 +++++++++ coreutils/link.c | 2 +- include/bb_archive.h | 4 +++ testsuite/tar.tests | 65 ++++++++++++++++++++++++++++------ 6 files changed, 130 insertions(+), 36 deletions(-) create mode 100755 archival/tar_symlink_attack 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) res = link(hard_link, dst_name); if (res != 0 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) { /* shared message */ - bb_perror_msg("can't create %slink " - "%s to %s", "hard", + bb_perror_msg("can't create %slink '%s' to '%s'", + "hard", dst_name, - hard_link); + hard_link + ); } /* Hardlinks have no separate mode/ownership, skip chown/chmod */ goto ret; @@ -178,15 +179,44 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) case S_IFLNK: /* Symlink */ //TODO: what if file_header->link_target == NULL (say, corrupted tarball?) + + /* To avoid a directory traversal attack via symlinks, + * for certain link targets postpone creation of symlinks. + * + * For example, consider a .tar created via: + * $ tar cvf bug.tar anything.txt + * $ ln -s /tmp symlink + * $ tar --append -f bug.tar symlink + * $ rm symlink + * $ mkdir symlink + * $ tar --append -f bug.tar symlink/evil.py + * + * This will result in an archive that contains: + * $ tar --list -f bug.tar + * anything.txt + * symlink [-> /tmp] + * symlink/evil.py + * + * Untarring bug.tar would otherwise place evil.py in '/tmp'. + */ + if (file_header->link_target[0] == '/' + || strstr(file_header->link_target, "..") + ) { + llist_add_to(&archive_handle->symlink_placeholders, + xasprintf("%s%c%s", file_header->name, '\0', file_header->link_target) + ); + break; + } res = symlink(file_header->link_target, dst_name); if (res != 0 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) ) { /* shared message */ - bb_perror_msg("can't create %slink " - "%s to %s", "sym", + bb_perror_msg("can't create %slink '%s' to '%s'", + "sym", dst_name, - file_header->link_target); + file_header->link_target + ); } break; case S_IFSOCK: diff --git a/archival/tar.c b/archival/tar.c index 0fc574dfd..280ded4e1 100644 --- a/archival/tar.c +++ b/archival/tar.c @@ -22,24 +22,6 @@ * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ -/* TODO: security with -C DESTDIR option can be enhanced. - * Consider tar file created via: - * $ tar cvf bug.tar anything.txt - * $ ln -s /tmp symlink - * $ tar --append -f bug.tar symlink - * $ rm symlink - * $ mkdir symlink - * $ tar --append -f bug.tar symlink/evil.py - * - * This will result in an archive which contains: - * $ tar --list -f bug.tar - * anything.txt - * symlink - * symlink/evil.py - * - * Untarring it puts evil.py in '/tmp' even if the -C DESTDIR is given. - * This doesn't feel right, and IIRC GNU tar doesn't do that. - */ //config:config TAR //config: bool "tar (40 kb)" @@ -296,6 +278,23 @@ static void chksum_and_xwrite(int fd, struct tar_header_t* hp) xwrite(fd, hp, sizeof(*hp)); } +static void replace_symlink_placeholders(llist_t *list) +{ + while (list) { + char *target; + + target = list->data + strlen(list->data) + 1; + if (symlink(target, list->data)) { + /* shared message */ + bb_error_msg_and_die("can't create %slink '%s' to '%s'", + "sym", + list->data, target + ); + } + list = list->link; + } +} + #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS static void writeLongname(int fd, int type, const char *name, int dir) { @@ -1252,6 +1251,8 @@ int tar_main(int argc UNUSED_PARAM, char **argv) while (get_header_tar(tar_handle) == EXIT_SUCCESS) bb_got_signal = EXIT_SUCCESS; /* saw at least one header, good */ + replace_symlink_placeholders(tar_handle->symlink_placeholders); + /* Check that every file that should have been extracted was */ while (tar_handle->accept) { 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 @@ +#!/bin/sh +# Makes "symlink attack" tarball (needs GNU tar for --append) + +true >anything.txt +tar cvf tar_symlink_attack.tar anything.txt +rm anything.txt + +ln -s /tmp symlink +tar --append -f tar_symlink_attack.tar symlink +rm symlink + +mkdir symlink +echo BUG >symlink/bb_test_evilfile +tar --append -f tar_symlink_attack.tar symlink/bb_test_evilfile +rm symlink/bb_test_evilfile +rmdir symlink 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) if (link(argv[0], argv[1]) != 0) { /* shared message */ bb_perror_msg_and_die("can't create %slink " - "%s to %s", "hard", + "'%s' to '%s'", "hard", argv[1], argv[0] ); } diff --git a/include/bb_archive.h b/include/bb_archive.h index 2b9c5f04c..d3762415f 100644 --- a/include/bb_archive.h +++ b/include/bb_archive.h @@ -64,6 +64,9 @@ typedef struct archive_handle_t { /* Currently processed file's header */ file_header_t *file_header; + /* List of symlink placeholders */ + llist_t *symlink_placeholders; + /* Process the header component, e.g. tar -t */ void FAST_FUNC (*action_header)(const file_header_t *); @@ -188,6 +191,7 @@ char get_header_ar(archive_handle_t *archive_handle) FAST_FUNC; char get_header_cpio(archive_handle_t *archive_handle) FAST_FUNC; char get_header_tar(archive_handle_t *archive_handle) FAST_FUNC; char get_header_tar_gz(archive_handle_t *archive_handle) FAST_FUNC; +char get_header_tar_xz(archive_handle_t *archive_handle) FAST_FUNC; char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC; char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC; char get_header_tar_xz(archive_handle_t *archive_handle) FAST_FUNC; 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 unset LC_ALL umask 022 -rm -rf tar.tempdir 2>/dev/null -mkdir tar.tempdir && cd tar.tempdir || exit 1 - # testing "test name" "script" "expected result" "file input" "stdin" 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 $? "" "" SKIP= +mkdir tar.tempdir && cd tar.tempdir || exit 1 # "tar cf test.tar input input_dir/ input_hard1 input_hard2 input_hard1 input_dir/ input": # GNU tar 1.26 records as hardlinks: # input_hard2 -> input_hard1 @@ -64,7 +62,6 @@ SKIP= # We also don't use "hrw-r--r--" notation for hardlinks in "tar tv" listing. optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES testing "tar hardlinks and repeated files" '\ -rm -rf input_* test.tar 2>/dev/null >input_hard1 ln input_hard1 input_hard2 mkdir input_dir @@ -95,10 +92,11 @@ drwxr-xr-x input_dir " \ "" "" SKIP= +cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null +mkdir tar.tempdir && cd tar.tempdir || exit 1 optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES testing "tar hardlinks mode" '\ -rm -rf input_* test.tar 2>/dev/null >input_hard1 chmod 741 input_hard1 ln input_hard1 input_hard2 @@ -128,10 +126,11 @@ Ok: 0 " \ "" "" SKIP= +cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null +mkdir tar.tempdir && cd tar.tempdir || exit 1 optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES testing "tar symlinks mode" '\ -rm -rf input_* test.tar 2>/dev/null >input_file chmod 741 input_file ln -s input_file input_soft @@ -159,10 +158,11 @@ lrwxrwxrwx input_file " \ "" "" SKIP= +cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null +mkdir tar.tempdir && cd tar.tempdir || exit 1 optional FEATURE_TAR_CREATE FEATURE_TAR_LONG_OPTIONS testing "tar --overwrite" "\ -rm -rf input_* test.tar 2>/dev/null ln input input_hard tar cf test.tar input_hard echo WRONG >input @@ -174,12 +174,13 @@ Ok " \ "Ok\n" "" SKIP= +cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null +mkdir tar.tempdir && cd tar.tempdir || exit 1 test x"$SKIP_KNOWN_BUGS" = x"" && { # Needs to be run under non-root for meaningful test optional FEATURE_TAR_CREATE testing "tar writing into read-only dir" '\ -rm -rf input_* test.tar 2>/dev/null mkdir input_dir >input_dir/input_file chmod 550 input_dir @@ -201,7 +202,9 @@ dr-xr-x--- input_dir "" "" SKIP= } +cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null +mkdir tar.tempdir && cd tar.tempdir || exit 1 # Had a bug where on extract autodetect first "switched off" -z # and then failed to recognize .tgz extension optional FEATURE_TAR_CREATE FEATURE_SEAMLESS_GZ GUNZIP @@ -217,7 +220,9 @@ Ok " \ "" "" SKIP= +cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null +mkdir tar.tempdir && cd tar.tempdir || exit 1 # Do we detect XZ-compressed data (even w/o .tar.xz or txz extension)? # (the uuencoded hello_world.txz contains one empty file named "hello_world") optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_XZ @@ -236,7 +241,9 @@ AAAEWVo= ==== " SKIP= +cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null +mkdir tar.tempdir && cd tar.tempdir || exit 1 # On extract, everything up to and including last ".." component is stripped optional FEATURE_TAR_CREATE testing "tar strips /../ on extract" "\ @@ -255,7 +262,9 @@ Ok " \ "" "" SKIP= +cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null +mkdir tar.tempdir && cd tar.tempdir || exit 1 # attack.tar.bz2 has symlink pointing to a system file # followed by a regular file with the same name # containing "root::0:0::/root:/bin/sh": @@ -270,6 +279,7 @@ optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2 testing "tar does not extract into symlinks" "\ >>/tmp/passwd && uudecode -o input && tar xf input 2>&1 && rm passwd; cat /tmp/passwd; echo \$? " "\ +tar: can't create symlink 'passwd' to '/tmp/passwd' 0 " \ "" "\ @@ -281,12 +291,15 @@ l4/V8LDoe90yiWJhOJvIypgEfxdyRThQkBVn/bI= ==== " SKIP= +cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null + +mkdir tar.tempdir && cd tar.tempdir || exit 1 # And same with -k optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2 testing "tar -k does not extract into symlinks" "\ >>/tmp/passwd && uudecode -o input && tar xf input -k 2>&1 && rm passwd; cat /tmp/passwd; echo \$? " "\ -tar: can't open 'passwd': File exists +tar: can't create symlink 'passwd' to '/tmp/passwd' 0 " \ "" "\ @@ -298,7 +311,9 @@ l4/V8LDoe90yiWJhOJvIypgEfxdyRThQkBVn/bI= ==== " SKIP= +cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null +mkdir tar.tempdir && cd tar.tempdir || exit 1 optional UNICODE_SUPPORT FEATURE_TAR_GNU_EXTENSIONS FEATURE_SEAMLESS_BZ2 FEATURE_TAR_AUTODETECT testing "Pax-encoded UTF8 names and symlinks" '\ 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 " \ "" "" SKIP= +cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null - -cd .. && rm -rf tar.tempdir || exit 1 +mkdir tar.tempdir && cd tar.tempdir || exit 1 +optional FEATURE_SEAMLESS_BZ2 FEATURE_TAR_AUTODETECT +testing "Symlink attack: create symlink and then write through it" '\ +exec 2>&1 +uudecode -o input && tar xvf input; echo $? +ls /tmp/bb_test_evilfile +ls bb_test_evilfile +ls symlink/bb_test_evilfile +' "\ +anything.txt +symlink +symlink/bb_test_evilfile +tar: can't create symlink 'symlink' to '/tmp' +1 +ls: /tmp/bb_test_evilfile: No such file or directory +ls: bb_test_evilfile: No such file or directory +symlink/bb_test_evilfile +" \ +"" "\ +begin-base64 644 tar_symlink_attack.tar.bz2 +QlpoOTFBWSZTWZgs7bQAALT/hMmQAFBAAf+AEMAGJPPv32AAAIAIMAC5thlR +omAjAmCMADQT1BqNE0AEwAAjAEwElTKeo9NTR6h6gaeoA0DQNLVdwZZ5iNTk +AQwCAV6S00QFJYhrlfFkVCEDEGtgNVqYrI0uK3ggnt30gqk4e1TTQm5QIAKa +SJqzRGSFLMmOloHSAcvLiFxxRiQtQZF+qPxbo173ZDISOAoNoPN4PQPhBhKS +n8fYaKlioCTzL2oXYczyUUIP4u5IpwoSEwWdtoA= +==== +" +SKIP= +cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null exit $FAILCOUNT -- cgit v1.2.3-55-g6feb From ca50caacad8354fe97eb0da23075521156c0c0d6 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 24 Jul 2017 18:51:40 +0200 Subject: shell: some additions to *sh-misc/* tests Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-misc/assignment2.right | 2 ++ shell/ash_test/ash-misc/assignment2.tests | 3 +++ shell/ash_test/ash-misc/empty_args.right | 6 ++++++ shell/ash_test/ash-misc/empty_args.tests | 9 +++++++++ shell/ash_test/ash-misc/env_and_func.right | 2 ++ shell/ash_test/ash-misc/env_and_func.tests | 8 ++++++++ shell/hush_test/hush-misc/env_and_func.tests | 4 ++++ 7 files changed, 34 insertions(+) create mode 100644 shell/ash_test/ash-misc/assignment2.right create mode 100755 shell/ash_test/ash-misc/assignment2.tests create mode 100644 shell/ash_test/ash-misc/empty_args.right create mode 100755 shell/ash_test/ash-misc/empty_args.tests create mode 100644 shell/ash_test/ash-misc/env_and_func.right create mode 100755 shell/ash_test/ash-misc/env_and_func.tests 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 @@ +./assignment2.tests: line 2: a=b: not found +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 @@ +# This must not be interpreted as an assignment +a''=b true +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 @@ +Null 0th arg: +./empty_args.tests: line 2: : Permission denied +127 +Null 1st arg: +0 +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 @@ +echo Null 0th arg: +"" +echo $? +echo Null 1st arg: +# printf without args would print usage info +printf "" +echo $? +echo Null arg in exec: +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 @@ +var=val +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 @@ +var=old +f() { echo "var=$var"; } +# bash: POSIXLY_CORRECT behavior is to "leak" new variable values +# out of function invocations (similar to "special builtins" behavior); +# but in "bash mode", they don't leak. +# hush does not "leak" values. ash does. +var=val f +echo "var=$var" 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 @@ var=old f() { echo "var=$var"; } +# bash: POSIXLY_CORRECT behavior is to "leak" new variable values +# out of function invocations (similar to "special builtins" behavior); +# but in "bash mode", they don't leak. +# hush does not "leak" values. ash does. var=val f echo "var=$var" -- cgit v1.2.3-55-g6feb From 8d2191c6aedf60c29dc0a6ee8c452fee7e460ee7 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 24 Jul 2017 19:42:46 +0200 Subject: ash: copy three tests from hush_test/hush-signals/* Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-signals/catch.right | 5 +++++ shell/ash_test/ash-signals/catch.tests | 20 ++++++++++++++++++++ shell/ash_test/ash-signals/signal_read2.right | 2 ++ shell/ash_test/ash-signals/signal_read2.tests | 7 +++++++ shell/ash_test/ash-signals/subshell.right | 21 +++++++++++++++++++++ shell/ash_test/ash-signals/subshell.tests | 19 +++++++++++++++++++ 6 files changed, 74 insertions(+) create mode 100644 shell/ash_test/ash-signals/catch.right create mode 100755 shell/ash_test/ash-signals/catch.tests create mode 100644 shell/ash_test/ash-signals/signal_read2.right create mode 100755 shell/ash_test/ash-signals/signal_read2.tests create mode 100644 shell/ash_test/ash-signals/subshell.right create mode 100755 shell/ash_test/ash-signals/subshell.tests 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 @@ +sending USR2 +caught +sending USR2 +sending USR2 +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 @@ +# avoid ugly warnings about signals not being caught +trap ":" USR1 USR2 + +"$THIS_SH" -c ' +trap "echo caught" USR2 +echo "sending USR2" +kill -USR2 $$ + +trap "" USR2 +echo "sending USR2" +kill -USR2 $$ + +trap "-" USR2 +echo "sending USR2" +kill -USR2 $$ + +echo "not reached" +' + +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 @@ +Hangup +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 @@ +$THIS_SH -c ' +(sleep 1; kill -HUP $$) & +while true; do + read ignored +done +' +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 @@ +trap -- '' HUP +trap -- '' QUIT +trap -- '' SYS +Ok +trap -- '' HUP +trap -- '' QUIT +trap -- '' SYS +Ok +trap -- '' HUP +trap -- '' QUIT +trap -- '' SYS +Ok +trap -- '' HUP +trap -- '' QUIT +trap -- '' SYS +Ok +trap -- '' HUP +trap -- '' QUIT +trap -- '' SYS +Terminated +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 @@ +# Non-empty traps should be reset in subshell + +# HUP is special in interactive shells +trap '' HUP +# QUIT is always special +trap '' QUIT +# SYS is not special +trap '' SYS +# WINCH is harmless +trap 'bad: caught WINCH' WINCH +# With TERM we'll check whether it is reset +trap 'bad: caught TERM' TERM + +(trap; "$THIS_SH" -c 'kill -HUP $PPID'; echo Ok) +(trap; "$THIS_SH" -c 'kill -QUIT $PPID'; echo Ok) +(trap; "$THIS_SH" -c 'kill -SYS $PPID'; echo Ok) +(trap; "$THIS_SH" -c 'kill -WINCH $PPID'; echo Ok) +(trap; "$THIS_SH" -c 'kill -TERM $PPID'; echo Bad: TERM is not reset) +echo Done -- cgit v1.2.3-55-g6feb From 5c123ac2082530ef4426737183ff76fe4595e1ff Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 24 Jul 2017 20:03:24 +0200 Subject: ash: fix comment, no code changes Signed-off-by: Denys Vlasenko --- shell/ash.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index c52637c92..0ae086e98 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -1788,9 +1788,6 @@ single_quote(const char *s) /* * Produce a possibly single quoted string suitable as input to the shell. - * If 'conditional' is nonzero, quoting is only done if the string contains - * non-shellsafe characters, or is identical to a shell keyword (reserved - * word); if it is zero, quoting is always done. * If quoting was done, the return string is allocated on the stack, * otherwise a pointer to the original string is returned. */ -- cgit v1.2.3-55-g6feb From 73c47f6c41c97fb452b4747088543f2c2166830a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 25 Jul 2017 14:22:08 +0200 Subject: volume_id: enable minix detection function old new delta volume_id_probe_minix - 87 +87 fs2 64 68 +4 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 1/0 up/down: 91/0) Total: 91 bytes Patch by wdlkmpx Signed-off-by: Denys Vlasenko --- util-linux/volume_id/minix.c | 91 +++++++++++++++++++++++++++++++ util-linux/volume_id/unused_minix.c | 82 ---------------------------- util-linux/volume_id/volume_id.c | 1 - util-linux/volume_id/volume_id_internal.h | 2 +- 4 files changed, 92 insertions(+), 84 deletions(-) create mode 100644 util-linux/volume_id/minix.c delete mode 100644 util-linux/volume_id/unused_minix.c diff --git a/util-linux/volume_id/minix.c b/util-linux/volume_id/minix.c new file mode 100644 index 000000000..c934f9ead --- /dev/null +++ b/util-linux/volume_id/minix.c @@ -0,0 +1,91 @@ +/* + * volume_id - reads filesystem label and uuid + * + * Copyright (C) 2005 Kay Sievers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +//config:config FEATURE_VOLUMEID_MINIX +//config: bool "minix filesystem" +//config: default y +//config: depends on VOLUMEID + +//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_MINIX) += minix.o + +#include "volume_id_internal.h" + +struct minix_super_block { + uint16_t s_ninodes; + uint16_t s_nzones; + uint16_t s_imap_blocks; + uint16_t s_zmap_blocks; + uint16_t s_firstdatazone; + uint16_t s_log_zone_size; + uint32_t s_max_size; + uint16_t s_magic; + uint16_t s_state; + uint32_t s_zones; +} PACKED; + +/* V3 minix super-block data on disk */ +struct minix3_super_block { + uint32_t s_ninodes; + uint16_t s_pad0; + uint16_t s_imap_blocks; + uint16_t s_zmap_blocks; + uint16_t s_firstdatazone; + uint16_t s_log_zone_size; + uint16_t s_pad1; + uint32_t s_max_size; + uint32_t s_zones; + uint16_t s_magic; + uint16_t s_pad2; + uint16_t s_blocksize; + uint8_t s_disk_version; +} PACKED; + +#define MINIX_SUPERBLOCK_OFFSET 0x400 + +int FAST_FUNC volume_id_probe_minix(struct volume_id *id /*, uint64_t off*/) +{ +#define off ((uint64_t)0) + struct minix_super_block *ms; + struct minix3_super_block *ms3; + + dbg("probing at offset 0x%llx", (unsigned long long) off); + + ms = volume_id_get_buffer(id, off + MINIX_SUPERBLOCK_OFFSET, 0x200); + if (ms == NULL) + return -1; + if (ms->s_magic == cpu_to_le16(0x137F)) /* minix V1 fs, 14 char names */ + goto found; + if (ms->s_magic == cpu_to_le16(0x138F)) /* minix V1 fs, 30 char names */ + goto found; + if (ms->s_magic == cpu_to_le16(0x2468)) /* minix V2 fs, 14 char names */ + goto found; + if (ms->s_magic == cpu_to_le16(0x2478)) /* minix V2 fs, 30 char names */ + goto found; + + ms3 = (void*)ms; + if (ms3->s_magic == cpu_to_le16(0x4d5a)) /* minix V3 fs, 60 char names */ + goto found; + + return -1; + + found: +// volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); + IF_FEATURE_BLKID_TYPE(id->type = "minix";) + return 0; +} diff --git a/util-linux/volume_id/unused_minix.c b/util-linux/volume_id/unused_minix.c deleted file mode 100644 index 443dbc272..000000000 --- a/util-linux/volume_id/unused_minix.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * volume_id - reads filesystem label and uuid - * - * Copyright (C) 2005 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -//kbuild:### lib-$(CONFIG_FEATURE_VOLUMEID_MINIX) += minix.o - -//config:### config FEATURE_VOLUMEID_MINIX -//config:### bool "minix filesystem" -//config:### default y -//config:### depends on VOLUMEID - -#include "volume_id_internal.h" - -struct minix_super_block { - uint16_t s_ninodes; - uint16_t s_nzones; - uint16_t s_imap_blocks; - uint16_t s_zmap_blocks; - uint16_t s_firstdatazone; - uint16_t s_log_zone_size; - uint32_t s_max_size; - uint16_t s_magic; - uint16_t s_state; - uint32_t s_zones; -} PACKED; - -#define MINIX_SUPERBLOCK_OFFSET 0x400 - -int FAST_FUNC volume_id_probe_minix(struct volume_id *id, uint64_t off) -{ - struct minix_super_block *ms; - - dbg("probing at offset 0x%llx", (unsigned long long) off); - - ms = volume_id_get_buffer(id, off + MINIX_SUPERBLOCK_OFFSET, 0x200); - if (ms == NULL) - return -1; - - if (ms->s_magic == cpu_to_le16(0x137f)) { -// id->type_version[0] = '1'; - goto found; - } - - if (ms->s_magic == cpu_to_le16(0x1387)) { -// id->type_version[0] = '1'; - goto found; - } - - if (ms->s_magic == cpu_to_le16(0x2468)) { -// id->type_version[0] = '2'; - goto found; - } - - if (ms->s_magic == cpu_to_le16(0x2478)) { -// id->type_version[0] = '2'; - goto found; - } - - return -1; - - found: -// id->type_version[1] = '\0'; -// volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); -// id->type = "minix"; - return 0; -} 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 @@ #define ENABLE_FEATURE_VOLUMEID_VIARAID 0 /* These filesystems also have no label or uuid: */ -#define ENABLE_FEATURE_VOLUMEID_MINIX 0 #define ENABLE_FEATURE_VOLUMEID_HPFS 0 #define ENABLE_FEATURE_VOLUMEID_UFS 0 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*/); //int FAST_FUNC volume_id_probe_mac_partition_map(struct volume_id *id /*,uint64_t off*/); -//int FAST_FUNC volume_id_probe_minix(struct volume_id *id /*,uint64_t off*/); +int FAST_FUNC volume_id_probe_minix(struct volume_id *id /*, uint64_t off*/); //int FAST_FUNC volume_id_probe_msdos_part_table(struct volume_id *id /*,uint64_t off*/); -- cgit v1.2.3-55-g6feb From 64925384c9cf5e0d986e183577da286bb3207ce7 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 25 Jul 2017 14:55:05 +0200 Subject: ash: add a few tests from hush-vars/* Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-vars/param_expand_alt.right | 9 +++ shell/ash_test/ash-vars/param_expand_alt.tests | 28 ++++++++ shell/ash_test/ash-vars/param_expand_assign.right | 27 +++++++ shell/ash_test/ash-vars/param_expand_assign.tests | 39 ++++++++++ .../ash-vars/param_expand_bash_substring.right | 64 +++++++++++++++++ .../ash-vars/param_expand_bash_substring.tests | 84 ++++++++++++++++++++++ shell/hush_test/hush-vars/param_expand_alt.right | 1 + shell/hush_test/hush-vars/param_expand_alt.tests | 14 ++-- .../hush_test/hush-vars/param_expand_assign.tests | 27 +++---- .../hush-vars/param_expand_bash_substring.tests | 13 ++-- 10 files changed, 283 insertions(+), 23 deletions(-) create mode 100644 shell/ash_test/ash-vars/param_expand_alt.right create mode 100755 shell/ash_test/ash-vars/param_expand_alt.tests create mode 100644 shell/ash_test/ash-vars/param_expand_assign.right create mode 100755 shell/ash_test/ash-vars/param_expand_assign.tests create mode 100644 shell/ash_test/ash-vars/param_expand_bash_substring.right create mode 100755 shell/ash_test/ash-vars/param_expand_bash_substring.tests 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..c733c147a --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_alt.right @@ -0,0 +1,9 @@ +SHELL: line 1: syntax error: bad substitution +SHELL: line 1: syntax error: bad substitution +_0_ __ +_z_ _z_ +_ _ _ _ _ +_aaaa _ _ _word _word +_ _ _ _ _ +_ _ _ _word _ +_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..c9c4249af --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_alt.tests @@ -0,0 +1,28 @@ +# First try some invalid patterns. Do in subshell due to parsing error. +# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) +"$THIS_SH" -c 'echo ${+} ; echo moo' SHELL +"$THIS_SH" -c 'echo ${:+} ; echo moo' SHELL + +# now some funky ones. +# ${V+word} "if V unset, then substitute nothing, else substitute word" +# ${V:+word} "if V unset or '', then substitute nothing, else substitute word" +# bash doesn't accept ${#+}. ash prints 0 (not $#). +echo _${#+}_ _${#:+}_ +# Forms with non-empty word work as expected in both ash and bash. +echo _${#+z}_ _${#:+z}_ + +# now some valid ones +set -- +echo _$1 _${1+} _${1:+} _${1+word} _${1:+word} + +set -- aaaa +echo _$1 _${1+} _${1:+} _${1+word} _${1:+word} + +unset f +echo _$f _${f+} _${f:+} _${f+word} _${f:+word} + +f= +echo _$f _${f+} _${f:+} _${f+word} _${f:+word} + +f=fff +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 @@ +SHELL: line 1: syntax error: bad substitution +SHELL: line 1: syntax error: bad substitution +0 +0 +SHELL: line 1: 1: bad variable name +SHELL: line 1: 1: bad variable name +SHELL: line 1: 1: bad variable name +SHELL: line 1: 1: bad variable name +_aa +_aa +_aa +_aa +_ +_ +_ +_word +_word +_ +_ +_ +_ +_word +_fff +_fff +_fff +_fff +_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 @@ +# First try some invalid patterns. Do in subshell due to parsing error. +# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) +"$THIS_SH" -c 'echo ${=}' SHELL +"$THIS_SH" -c 'echo ${:=}' SHELL + +# now some funky ones +"$THIS_SH" -c 'echo ${#=}' SHELL +"$THIS_SH" -c 'echo ${#:=}' SHELL + +# should error out +"$THIS_SH" -c 'set --; echo _${1=}' SHELL +"$THIS_SH" -c 'set --; echo _${1:=}' SHELL +"$THIS_SH" -c 'set --; echo _${1=word}' SHELL +"$THIS_SH" -c 'set --; echo _${1:=word}' SHELL + +# should not error +"$THIS_SH" -c 'set aa; echo _${1=}' SHELL +"$THIS_SH" -c 'set aa; echo _${1:=}' SHELL +"$THIS_SH" -c 'set aa; echo _${1=word}' SHELL +"$THIS_SH" -c 'set aa; echo _${1:=word}' SHELL + +# should work fine +unset f; echo _$f +unset f; echo _${f=} +unset f; echo _${f:=} +unset f; echo _${f=word} +unset f; echo _${f:=word} + +f=; echo _$f +f=; echo _${f=} +f=; echo _${f:=} +f=; echo _${f=word} +f=; echo _${f:=word} + +f=fff; echo _$f +f=fff; echo _${f=} +f=fff; echo _${f:=} +f=fff; echo _${f=word} +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 @@ +SHELL: line 1: syntax error: bad substitution +SHELL: line 1: syntax error: bad substitution +SHELL: line 1: syntax error: bad substitution +SHELL: line 1: syntax error: bad substitution +SHELL: line 1: syntax error: missing '}' +1 =|| +1:1 =|| +1:1:2=|| +1::2 =|| +1:1: =|| +1:: =|| +1 =|0123| +1:1 =|123| +1:1:2=|12| +1::2 =|01| +1:1: =|| +1:: =|| +f =|| +f:1 =|| +f:1:2=|| +f::2 =|| +f:1: =|| +f:: =|| +f =|| +f:1 =|| +f:1:2=|| +f::2 =|| +f:1: =|| +f:: =|| +f =|a| +f:1 =|| +f:1:2=|| +f::2 =|a| +f:1: =|| +f:: =|| +f =|0123456789| +f:1 =|123456789| +f:1:2=|12| +f::2 =|01| +f:1: =|| +f:: =|| +Substrings from special vars +? =|0| +?:1 =|| +?:1:2=|| +?::2 =|0| +?:1: =|| +?:: =|| +# =|11| +#:1 =|1| +#:1:2=|1| +#::2 =|11| +#:1: =|| +#:: =|| +Substrings with expressions +f =|01234567| +f:1+1:2+2 =|2345| +f:-1:2+2 =|01234567| +f:1:f =|1234567| +f:1:$f =|1234567| +f:1:${f} =|1234567| +f:1:${f:3:1} =|123| +f:1:1`echo 1`=|1| +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 @@ +# first try some invalid patterns +# do all of these in subshells since it's supposed to error out +# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) +export var=0123456789 +"$THIS_SH" -c 'echo ${:}' SHELL +"$THIS_SH" -c 'echo ${::}' SHELL +"$THIS_SH" -c 'echo ${:1}' SHELL +"$THIS_SH" -c 'echo ${::1}' SHELL + +#this also is not valid in bash, hush accepts it: +"$THIS_SH" -c 'echo ${var:}' SHELL + +# then some funky ones +# UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}' + +# now some valid ones +set --; echo "1 =|${1}|" +set --; echo "1:1 =|${1:1}|" +set --; echo "1:1:2=|${1:1:2}|" +set --; echo "1::2 =|${1::2}|" +set --; echo "1:1: =|${1:1:}|" +set --; echo "1:: =|${1::}|" + +set -- 0123; echo "1 =|${1}|" +set -- 0123; echo "1:1 =|${1:1}|" +set -- 0123; echo "1:1:2=|${1:1:2}|" +set -- 0123; echo "1::2 =|${1::2}|" +set -- 0123; echo "1:1: =|${1:1:}|" +set -- 0123; echo "1:: =|${1::}|" + +unset f; echo "f =|$f|" +unset f; echo "f:1 =|${f:1}|" +unset f; echo "f:1:2=|${f:1:2}|" +unset f; echo "f::2 =|${f::2}|" +unset f; echo "f:1: =|${f:1:}|" +unset f; echo "f:: =|${f::}|" + +f=; echo "f =|$f|" +f=; echo "f:1 =|${f:1}|" +f=; echo "f:1:2=|${f:1:2}|" +f=; echo "f::2 =|${f::2}|" +f=; echo "f:1: =|${f:1:}|" +f=; echo "f:: =|${f::}|" + +f=a; echo "f =|$f|" +f=a; echo "f:1 =|${f:1}|" +f=a; echo "f:1:2=|${f:1:2}|" +f=a; echo "f::2 =|${f::2}|" +f=a; echo "f:1: =|${f:1:}|" +f=a; echo "f:: =|${f::}|" + +f=0123456789; echo "f =|$f|" +f=0123456789; echo "f:1 =|${f:1}|" +f=0123456789; echo "f:1:2=|${f:1:2}|" +f=0123456789; echo "f::2 =|${f::2}|" +f=0123456789; echo "f:1: =|${f:1:}|" +f=0123456789; echo "f:: =|${f::}|" + +echo "Substrings from special vars" +echo '? '"=|$?|" +echo '?:1 '"=|${?:1}|" +echo '?:1:2'"=|${?:1:2}|" +echo '?::2 '"=|${?::2}|" +echo '?:1: '"=|${?:1:}|" +echo '?:: '"=|${?::}|" +set -- 1 2 3 4 5 6 7 8 9 10 11 +echo '# '"=|$#|" +echo '#:1 '"=|${#:1}|" +echo '#:1:2'"=|${#:1:2}|" +echo '#::2 '"=|${#::2}|" +echo '#:1: '"=|${#:1:}|" +echo '#:: '"=|${#::}|" + +echo "Substrings with expressions" +f=01234567; echo 'f '"=|$f|" +f=01234567; echo 'f:1+1:2+2 '"=|${f:1+1:2+2}|" +f=01234567; echo 'f:-1:2+2 '"=|${f:-1:2+2}|" +f=01234567; echo 'f:1:f '"=|${f:1:f}|" +f=01234567; echo 'f:1:$f '"=|${f:1:$f}|" +f=01234567; echo 'f:1:${f} '"=|${f:1:${f}}|" +f=01234567; echo 'f:1:${f:3:1} '"=|${f:1:${f:3:1}}|" +f=01234567; echo 'f:1:1`echo 1`'"=|${f:1:`echo 1`}|" + +echo Done diff --git a/shell/hush_test/hush-vars/param_expand_alt.right b/shell/hush_test/hush-vars/param_expand_alt.right index 67f18d69c..4f9eb2907 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 @@ hush: syntax error: unterminated ${name} hush: syntax error: unterminated ${name} __ __ +_z_ _z_ _ _ _ _ _ _aaaa _ _ _word _word _ _ _ _ _ diff --git a/shell/hush_test/hush-vars/param_expand_alt.tests b/shell/hush_test/hush-vars/param_expand_alt.tests index 3b646b142..c9c4249af 100755 --- a/shell/hush_test/hush-vars/param_expand_alt.tests +++ b/shell/hush_test/hush-vars/param_expand_alt.tests @@ -1,9 +1,15 @@ -# first try some invalid patterns (do in subshell due to parsing error) -"$THIS_SH" -c 'echo ${+} ; echo moo' -"$THIS_SH" -c 'echo ${:+} ; echo moo' +# First try some invalid patterns. Do in subshell due to parsing error. +# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) +"$THIS_SH" -c 'echo ${+} ; echo moo' SHELL +"$THIS_SH" -c 'echo ${:+} ; echo moo' SHELL -# now some funky ones. (bash doesn't accept ${#+}) +# now some funky ones. +# ${V+word} "if V unset, then substitute nothing, else substitute word" +# ${V:+word} "if V unset or '', then substitute nothing, else substitute word" +# bash doesn't accept ${#+}. ash prints 0 (not $#). echo _${#+}_ _${#:+}_ +# Forms with non-empty word work as expected in both ash and bash. +echo _${#+z}_ _${#:+z}_ # now some valid ones 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 @@ -# first try some invalid patterns (do in subshell due to parsing error) -"$THIS_SH" -c 'echo ${=}' -"$THIS_SH" -c 'echo ${:=}' +# First try some invalid patterns. Do in subshell due to parsing error. +# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) +"$THIS_SH" -c 'echo ${=}' SHELL +"$THIS_SH" -c 'echo ${:=}' SHELL # now some funky ones -"$THIS_SH" -c 'echo ${#=}' -"$THIS_SH" -c 'echo ${#:=}' +"$THIS_SH" -c 'echo ${#=}' SHELL +"$THIS_SH" -c 'echo ${#:=}' SHELL # should error out -"$THIS_SH" -c 'set --; echo _${1=}' -"$THIS_SH" -c 'set --; echo _${1:=}' -"$THIS_SH" -c 'set --; echo _${1=word}' -"$THIS_SH" -c 'set --; echo _${1:=word}' +"$THIS_SH" -c 'set --; echo _${1=}' SHELL +"$THIS_SH" -c 'set --; echo _${1:=}' SHELL +"$THIS_SH" -c 'set --; echo _${1=word}' SHELL +"$THIS_SH" -c 'set --; echo _${1:=word}' SHELL # should not error -"$THIS_SH" -c 'set aa; echo _${1=}' -"$THIS_SH" -c 'set aa; echo _${1:=}' -"$THIS_SH" -c 'set aa; echo _${1=word}' -"$THIS_SH" -c 'set aa; echo _${1:=word}' +"$THIS_SH" -c 'set aa; echo _${1=}' SHELL +"$THIS_SH" -c 'set aa; echo _${1:=}' SHELL +"$THIS_SH" -c 'set aa; echo _${1=word}' SHELL +"$THIS_SH" -c 'set aa; echo _${1:=word}' SHELL # should work fine 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 @@ # first try some invalid patterns # do all of these in subshells since it's supposed to error out +# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) export var=0123456789 -"$THIS_SH" -c 'echo ${:}' -"$THIS_SH" -c 'echo ${::}' -"$THIS_SH" -c 'echo ${:1}' -"$THIS_SH" -c 'echo ${::1}' +"$THIS_SH" -c 'echo ${:}' SHELL +"$THIS_SH" -c 'echo ${::}' SHELL +"$THIS_SH" -c 'echo ${:1}' SHELL +"$THIS_SH" -c 'echo ${::1}' SHELL -#this also is not valid in bash, but we accept it: -"$THIS_SH" -c 'echo ${var:}' +#this also is not valid in bash, hush accepts it: +"$THIS_SH" -c 'echo ${var:}' SHELL # then some funky ones # UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}' -- cgit v1.2.3-55-g6feb From 645c697372b714f1293a37a185aa62965f600479 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 25 Jul 2017 15:18:57 +0200 Subject: hush: treat ${#?} as "length of $?" Signed-off-by: Denys Vlasenko --- .../ash-vars/param_expand_indicate_error.right | 43 +++++++++++++++ .../ash-vars/param_expand_indicate_error.tests | 61 ++++++++++++++++++++++ shell/hush.c | 12 +++-- .../hush-vars/param_expand_indicate_error.right | 2 +- .../hush-vars/param_expand_indicate_error.tests | 2 +- 5 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 shell/ash_test/ash-vars/param_expand_indicate_error.right create mode 100755 shell/ash_test/ash-vars/param_expand_indicate_error.tests 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 @@ +SHELL: line 1: syntax error: bad substitution +1 +0 +==== +_ +SHELL: line 1: 1: parameter not set +SHELL: line 1: 1: parameter not set or null +SHELL: line 1: 1: message1 +SHELL: line 1: 1: message1 +SHELL: line 1: 1: unset! +SHELL: line 1: 1: null or unset! +==== +_aaaa +_aaaa +_aaaa +_aaaa +_aaaa +_aaaa +_aaaa +==== +_ +SHELL: line 1: f: parameter not set +SHELL: line 1: f: parameter not set or null +SHELL: line 1: f: message3 +SHELL: line 1: f: message3 +SHELL: line 1: f: unset! +SHELL: line 1: f: null or unset! +==== +_ +_ +SHELL: line 1: f: parameter not set or null +_ +SHELL: line 1: f: message4 +_ +SHELL: line 1: f: null or unset! +==== +_fff +_fff +_fff +_fff +_fff +_fff +_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 @@ +# do all of these in subshells since it's supposed to error out +# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) + +# first try some invalid patterns +#"$THIS_SH" -c 'echo ${?}' SHELL -- this is valid as it's the same as $? +"$THIS_SH" -c 'echo ${:?}' SHELL + +# then some funky ones +# note: bash prints 1 - treats it as "length of $#" +"$THIS_SH" -c 'echo ${#?}' SHELL +# bash prints 0 +"$THIS_SH" -c 'echo ${#:?}' SHELL + +# now some valid ones +export msg_unset="unset!" +export msg_null_or_unset="null or unset!" + +echo ==== +"$THIS_SH" -c 'set --; echo _$1' SHELL +"$THIS_SH" -c 'set --; echo _${1?}' SHELL +"$THIS_SH" -c 'set --; echo _${1:?}' SHELL +"$THIS_SH" -c 'set --; echo _${1?message1}' SHELL +"$THIS_SH" -c 'set --; echo _${1:?message1}' SHELL +"$THIS_SH" -c 'set --; echo _${1?$msg_unset}' SHELL +"$THIS_SH" -c 'set --; echo _${1:?$msg_null_or_unset}' SHELL + +echo ==== +"$THIS_SH" -c 'set -- aaaa; echo _$1' SHELL +"$THIS_SH" -c 'set -- aaaa; echo _${1?}' SHELL +"$THIS_SH" -c 'set -- aaaa; echo _${1:?}' SHELL +"$THIS_SH" -c 'set -- aaaa; echo _${1?word}' SHELL +"$THIS_SH" -c 'set -- aaaa; echo _${1:?word}' SHELL +"$THIS_SH" -c 'set -- aaaa; echo _${1?$msg_unset}' SHELL +"$THIS_SH" -c 'set -- aaaa; echo _${1:?$msg_null_or_unset}' SHELL + +echo ==== +"$THIS_SH" -c 'unset f; echo _$f' SHELL +"$THIS_SH" -c 'unset f; echo _${f?}' SHELL +"$THIS_SH" -c 'unset f; echo _${f:?}' SHELL +"$THIS_SH" -c 'unset f; echo _${f?message3}' SHELL +"$THIS_SH" -c 'unset f; echo _${f:?message3}' SHELL +"$THIS_SH" -c 'unset f; echo _${f?$msg_unset}' SHELL +"$THIS_SH" -c 'unset f; echo _${f:?$msg_null_or_unset}' SHELL + +echo ==== +"$THIS_SH" -c 'f=; echo _$f' SHELL +"$THIS_SH" -c 'f=; echo _${f?}' SHELL +"$THIS_SH" -c 'f=; echo _${f:?}' SHELL +"$THIS_SH" -c 'f=; echo _${f?word}' SHELL +"$THIS_SH" -c 'f=; echo _${f:?message4}' SHELL +"$THIS_SH" -c 'f=; echo _${f?$msg_unset}' SHELL +"$THIS_SH" -c 'f=; echo _${f:?$msg_null_or_unset}' SHELL + +echo ==== +"$THIS_SH" -c 'f=fff; echo _$f' SHELL +"$THIS_SH" -c 'f=fff; echo _${f?}' SHELL +"$THIS_SH" -c 'f=fff; echo _${f:?}' SHELL +"$THIS_SH" -c 'f=fff; echo _${f?word}' SHELL +"$THIS_SH" -c 'f=fff; echo _${f:?word}' SHELL +"$THIS_SH" -c 'f=fff; echo _${f?$msg_unset}' SHELL +"$THIS_SH" -c 'f=fff; echo _${f:?$msg_null_or_unset}' SHELL diff --git a/shell/hush.c b/shell/hush.c index 20b092398..f9dad074f 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -5559,8 +5559,10 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha first_char = arg[0] = arg0 & 0x7f; exp_op = 0; - if (first_char == '#' /* ${#... */ - && arg[1] && !exp_saveptr /* not ${#} and not ${#...} */ + if (first_char == '#' && arg[1] /* ${#... but not ${#} */ + && (!exp_saveptr /* and (not ${#...} */ + || (arg[1] == '?' && arg[2] == '\0') /* or ${#?} - "len of $?") */ + ) ) { /* It must be length operator: ${#var} */ var++; @@ -5797,7 +5799,11 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha /* mimic bash message */ die_if_script("%s: %s", var, - exp_word[0] ? exp_word : "parameter null or not set" + exp_word[0] + ? exp_word + : "parameter null or not set" + /* ash has more specific messages, a-la: */ + /*: (exp_save == ':' ? "parameter null or not set" : "parameter not set")*/ ); //TODO: how interactive bash aborts expansion mid-command? } else { 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 @@ hush: syntax error: unterminated ${name} -0 +1 0 ==== _ 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 @@ "$THIS_SH" -c 'echo ${:?}' # then some funky ones -# note: bash prints 1 - treats it as "length of $#"? We print 0 +# note: bash prints 1 - treats it as "length of $#" "$THIS_SH" -c 'echo ${#?}' # bash prints 0 "$THIS_SH" -c 'echo ${#:?}' -- cgit v1.2.3-55-g6feb From be669fa1fdff6f751c8cdd3fc18a9fa7a7f46cd3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 25 Jul 2017 15:25:07 +0200 Subject: ash: import param_expand_default.tests from hush Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-vars/param_expand_default.right | 7 +++++++ shell/ash_test/ash-vars/param_expand_default.tests | 23 ++++++++++++++++++++++ .../hush_test/hush-vars/param_expand_default.tests | 6 ++++-- 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 shell/ash_test/ash-vars/param_expand_default.right create mode 100755 shell/ash_test/ash-vars/param_expand_default.tests 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 @@ +SHELL: line 1: syntax error: bad substitution +_0 _0 +_ _ _ _word _word +_aaaa _aaaa _aaaa _aaaa _aaaa +_ _ _ _word _word +_ _ _ _ _word +_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 @@ +# first try some invalid patterns (do in subshell due to parsing error) +# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) +# valid in bash and ash (same as $-): "$THIS_SH" -c 'echo ${-}' SHELL +"$THIS_SH" -c 'echo ${:-}' SHELL + +# now some funky ones +echo _${#-} _${#:-} + +# now some valid ones +set -- +echo _$1 _${1-} _${1:-} _${1-word} _${1:-word} + +set -- aaaa +echo _$1 _${1-} _${1:-} _${1-word} _${1:-word} + +unset f +echo _$f _${f-} _${f:-} _${f-word} _${f:-word} + +f= +echo _$f _${f-} _${f:-} _${f-word} _${f:-word} + +f=fff +echo _$f _${f-} _${f:-} _${f-word} _${f:-word} 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 @@ # first try some invalid patterns (do in subshell due to parsing error) -"$THIS_SH" -c 'echo ${-}' -"$THIS_SH" -c 'echo ${:-}' +# (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) +# valid in bash and ash (same as $-), not supported in hush (yet?): +"$THIS_SH" -c 'echo ${-}' SHELL +"$THIS_SH" -c 'echo ${:-}' SHELL # now some funky ones echo _${#-} _${#:-} -- cgit v1.2.3-55-g6feb From b28d4c3462e6b0e66322503f5ef0b941e0bb9cb8 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 25 Jul 2017 16:29:36 +0200 Subject: ash: [VAR] Move unsetvar functionality into setvareq Upstream commit: Date: Tue, 25 May 2010 20:55:05 +0800 [VAR] Move unsetvar functionality into setvareq This patch moves the unsetvar code into setvareq so that we can no have a pathological case of an unset variable hanging around unless it has a bit pinning it like VEXPORT. Signed-off-by: Herbert Xu function old new delta setvareq 227 303 +76 expmeta 517 521 +4 localcmd 364 366 +2 unsetcmd 96 76 -20 unsetvar 129 7 -122 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/2 up/down: 82/-142) Total: -60 bytes Signed-off-by: Denys Vlasenko --- shell/ash.c | 57 +++++++++------------------------ shell/ash_test/ash-vars/readonly0.right | 2 +- shell/ash_test/ash-vars/unset.right | 17 ++++++++++ shell/ash_test/ash-vars/unset.tests | 40 +++++++++++++++++++++++ shell/hush_test/hush-vars/unset.right | 4 +-- shell/hush_test/hush-vars/unset.tests | 7 ++-- 6 files changed, 81 insertions(+), 46 deletions(-) create mode 100644 shell/ash_test/ash-vars/unset.right create mode 100755 shell/ash_test/ash-vars/unset.tests diff --git a/shell/ash.c b/shell/ash.c index 0ae086e98..72ceba782 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -2269,11 +2269,22 @@ setvareq(char *s, int flags) if (!(vp->flags & (VTEXTFIXED|VSTACK))) free((char*)vp->var_text); + if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) { + *vpp = vp->next; + free(vp); + out_free: + if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE) + free(s); + return; + } + flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET); } else { /* variable s is not found */ if (flags & VNOSET) return; + if ((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET) + goto out_free; vp = ckzalloc(sizeof(*vp)); vp->next = *vpp; /*vp->func = NULL; - ckzalloc did it */ @@ -2331,43 +2342,10 @@ setvar0(const char *name, const char *val) /* * Unset the specified variable. */ -static int +static void unsetvar(const char *s) { - struct var **vpp; - struct var *vp; - int retval; - - vpp = findvar(hashvar(s), s); - vp = *vpp; - retval = 2; - if (vp) { - int flags = vp->flags; - - retval = 1; - if (flags & VREADONLY) - goto out; -#if ENABLE_ASH_RANDOM_SUPPORT - vp->flags &= ~VDYNAMIC; -#endif - if (flags & VUNSET) - goto ok; - if ((flags & VSTRFIXED) == 0) { - INT_OFF; - if ((flags & (VTEXTFIXED|VSTACK)) == 0) - free((char*)vp->var_text); - *vpp = vp->next; - free(vp); - INT_ON; - } else { - setvar0(s, NULL); - vp->flags &= ~VEXPORT; - } - ok: - retval = 0; - } - out: - return retval; + setvar0(s, NULL); } /* @@ -13218,7 +13196,6 @@ unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) char **ap; int i; int flag = 0; - int ret = 0; while ((i = nextopt("vf")) != 0) { flag = i; @@ -13226,15 +13203,13 @@ unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) for (ap = argptr; *ap; ap++) { if (flag != 'f') { - i = unsetvar(*ap); - ret |= i; - if (!(i & 2)) - continue; + unsetvar(*ap); + continue; } if (flag != 'v') unsetfunc(*ap); } - return ret & 1; + return 0; } static const unsigned char timescmd_str[] ALIGN1 = { 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 ./readonly0.tests: export: line 27: a: is read only Fail:2 -Fail:1 +./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 @@ +./unset.tests: unset: line 3: -: bad variable name +2 +./unset.tests: unset: line 5: illegal option -m +2 +0 +___ +0 f g +0 g +0 +___ +0 f g +0 +0 f g +0 +___ +./unset.tests: unset: line 36: VAR_RO: is read only +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 @@ +# check invalid options are rejected +# bash: in posix mode, aborts if non-interactive; using subshell to avoid that +(unset -) +echo $? +(unset -m a b c) +echo $? + +# check funky usage +unset +echo $? + +# check normal usage +echo ___ +f=f g=g +echo $? $f $g +unset f +echo $? $f $g +unset g +echo $? $f $g + +echo ___ +f=f g=g +echo $? $f $g +unset f g +echo $? $f $g +f=f g=g +echo $? $f $g +unset -v f g +echo $? $f $g + +# check read only vars +echo ___ +f=f g=g +VAR_RO=1 +readonly VAR_RO +(unset VAR_RO) +echo $? $f $g +# not testing "do variables survive error halfway through unset" since unset aborts +# unset f VAR_RO g +#echo $? $f $g 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 @@ ___ 0 f g 0 ___ -hush: HUSH_VERSION: readonly variable +hush: VAR_RO: readonly variable 1 f g -hush: HUSH_VERSION: readonly variable +hush: VAR_RO: readonly variable 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 @@ # check invalid options are rejected +# bash: in posix mode, aborts if non-interactive unset - echo $? unset -m a b c @@ -30,7 +31,9 @@ echo $? $f $g # check read only vars echo ___ f=f g=g -unset HUSH_VERSION +VAR_RO=1 +readonly VAR_RO +unset VAR_RO echo $? $f $g -unset f HUSH_VERSION g +unset f VAR_RO g echo $? $f $g -- cgit v1.2.3-55-g6feb From 2990aa45d188b1d9814c89dd44658f068eb37e83 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 25 Jul 2017 17:37:57 +0200 Subject: ash: sync up with dash with respect to redirection escaping We fixed the problem differently than they. Let's not deviate. Upstream commit: Date: Thu, 27 May 2010 20:07:29 +1000 [EXPAND] Fix corruption of redirections with byte 0x81 In other ash variants, a partial implementation of ksh-like cmd >file* adds and removes CTLESC bytes ('\x81') in redirection filenames, preserving 8-bit transparency. Long ago, dash removed the code to add the CTLESC bytes, but not the code to remove them, causing corruption of filenames containing CTLESC. This commit removes the code to remove the CTLESC bytes. The CTLESC byte occurs frequently in UTF-8 encoded non-Latin text. This bug has been reported various times to Ubuntu and Debian (e.g. Launchpad Ubuntu #422298). This patch is the same as the one submitted by Alexander Korolkov in Ubuntu #422298. Signed-off-by: Jilles Tjoelker Signed-off-by: Herbert Xu function old new delta changepath 194 192 -2 expandarg 1000 984 -16 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-18) Total: -18 bytes Signed-off-by: Denys Vlasenko --- shell/ash.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 72ceba782..c353834a4 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -5711,7 +5711,7 @@ ash_arith(const char *s) #define RMESCAPE_SLASH 0x20 /* Stop globbing after slash */ /* Add CTLESC when necessary. */ -#define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT | EXP_REDIR) +#define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT) /* Do not skip NUL characters. */ #define QUOTES_KEEPNUL EXP_TILDE @@ -7608,10 +7608,6 @@ expandarg(union node *arg, struct arglist *arglist, int flag) exparg.lastp = &exparg.list; expandmeta(exparg.list /*, flag*/); } else { - if (flag & EXP_REDIR) { /*XXX - for now, just remove escapes */ - rmescapes(p, 0); - TRACE(("expandarg: rmescapes:'%s'\n", p)); - } sp = stzalloc(sizeof(*sp)); sp->text = p; *exparg.lastp = sp; -- cgit v1.2.3-55-g6feb From f1a5cb0548f647e628032ea8645c0d0d2d07b02f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 25 Jul 2017 17:47:48 +0200 Subject: ash: [REDIR] Replace GPL noclobberopen code with the FreeBSD version Upstream commit: Date: Thu, 10 Mar 2011 16:52:13 +0800 [REDIR] Replace GPL noclobberopen code with the FreeBSD version Replace noclobberopen() from bash with the FreeBSD code for noclobber opens. This also reduces code size by eliminating an unnecessary check. Signed-off-by: Herbert Xu function old new delta changepath 192 194 +2 localcmd 366 364 -2 expmeta 521 517 -4 redirect 1210 1135 -75 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/3 up/down: 2/-81) Total: -79 bytes Signed-off-by: Denys Vlasenko --- shell/ash.c | 81 +++++++++++++------------------------------------------------ 1 file changed, 17 insertions(+), 64 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index c353834a4..b4b0d5253 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -5174,68 +5174,6 @@ stoppedjobs(void) #define EMPTY -2 /* marks an unused slot in redirtab */ #define CLOSED -3 /* marks a slot of previously-closed fd */ -/* - * Open a file in noclobber mode. - * The code was copied from bash. - */ -static int -noclobberopen(const char *fname) -{ - int r, fd; - struct stat finfo, finfo2; - - /* - * If the file exists and is a regular file, return an error - * immediately. - */ - r = stat(fname, &finfo); - if (r == 0 && S_ISREG(finfo.st_mode)) { - errno = EEXIST; - return -1; - } - - /* - * If the file was not present (r != 0), make sure we open it - * exclusively so that if it is created before we open it, our open - * will fail. Make sure that we do not truncate an existing file. - * Note that we don't turn on O_EXCL unless the stat failed -- if the - * file was not a regular file, we leave O_EXCL off. - */ - if (r != 0) - return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666); - fd = open(fname, O_WRONLY|O_CREAT, 0666); - - /* If the open failed, return the file descriptor right away. */ - if (fd < 0) - return fd; - - /* - * OK, the open succeeded, but the file may have been changed from a - * non-regular file to a regular file between the stat and the open. - * We are assuming that the O_EXCL open handles the case where FILENAME - * did not exist and is symlinked to an existing file between the stat - * and open. - */ - - /* - * If we can open it and fstat the file descriptor, and neither check - * revealed that it was a regular file, and the file has not been - * replaced, return the file descriptor. - */ - if (fstat(fd, &finfo2) == 0 - && !S_ISREG(finfo2.st_mode) - && finfo.st_dev == finfo2.st_dev - && finfo.st_ino == finfo2.st_ino - ) { - return fd; - } - - /* The file has been replaced. badness. */ - close(fd); - errno = EEXIST; - return -1; -} - /* * Handle here documents. Normally we fork off a process to write the * data to a pipe. If the document is short, we can stuff the data in @@ -5280,6 +5218,7 @@ openhere(union node *redir) static int openredirect(union node *redir) { + struct stat sb; char *fname; int f; @@ -5319,9 +5258,23 @@ openredirect(union node *redir) #endif /* Take care of noclobber mode. */ if (Cflag) { - f = noclobberopen(fname); - if (f < 0) + if (stat(fname, &sb) < 0) { + f = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666); + if (f < 0) + goto ecreate; + } else if (!S_ISREG(sb.st_mode)) { + f = open(fname, O_WRONLY, 0666); + if (f < 0) + goto ecreate; + if (fstat(f, &sb) < 0 && S_ISREG(sb.st_mode)) { + close(f); + errno = EEXIST; + goto ecreate; + } + } else { + errno = EEXIST; goto ecreate; + } break; } /* FALLTHROUGH */ -- cgit v1.2.3-55-g6feb From 86981e3ad2d03e77d1f668ac1603a041be448dae Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 25 Jul 2017 20:06:17 +0200 Subject: ash: allow "trap NUM [SIG]..." syntax While at it, make get_signum() return -1 for numeric strings >= NSIG. function old new delta trapcmd 292 306 +14 get_signum 295 300 +5 builtin_trap 413 412 -1 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/1 up/down: 19/-1) Total: 18 bytes Signed-off-by: Denys Vlasenko --- libbb/u_signal_names.c | 2 +- procps/kill.c | 2 +- shell/ash.c | 9 +++++++-- shell/hush.c | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) 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) unsigned i; i = bb_strtou(name, NULL, 10); - if (!errno) + if (!errno && i < NSIG) /* for shells, we allow 0 too */ return i; if (strncasecmp(name, "SIG", 3) == 0) name += 3; 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) arg = *++argv; } /* else it must be -SIG */ signo = get_signum(arg); - if (signo < 0) { /* || signo > MAX_SIGNUM ? */ + if (signo < 0) { bb_error_msg("bad signal name '%s'", arg); return EXIT_FAILURE; } diff --git a/shell/ash.c b/shell/ash.c index b4b0d5253..42e14cbc8 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -12981,13 +12981,18 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) return 0; } + /* Why the second check? + * "trap NUM [sig2]..." is the same as "trap - NUM [sig2]..." + * In this case, NUM is signal no, not an action. + */ action = NULL; - if (ap[1]) + if (ap[1] && !is_number(ap[0])) action = *ap++; + exitcode = 0; while (*ap) { signo = get_signum(*ap); - if (signo < 0 || signo >= NSIG) { + if (signo < 0) { /* Mimic bash message exactly */ ash_msg("%s: invalid signal specification", *ap); exitcode = 1; diff --git a/shell/hush.c b/shell/hush.c index f9dad074f..11b33f40a 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -9745,7 +9745,7 @@ static int FAST_FUNC builtin_trap(char **argv) sighandler_t handler; sig = get_signum(*argv++); - if (sig < 0 || sig >= NSIG) { + if (sig < 0) { ret = EXIT_FAILURE; /* Mimic bash message exactly */ bb_error_msg("trap: %s: invalid signal specification", argv[-1]); -- cgit v1.2.3-55-g6feb From 1e3e2ccd5dd280371c9ca29c0e0304a0d40592af Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 25 Jul 2017 20:31:14 +0200 Subject: ash: [SHELL] Optimize dash -c "command" to avoid a fork Upstream commit: Date: Thu, 7 Jul 2011 13:58:48 +0800 [SHELL] Optimize dash -c "command" to avoid a fork On Sun, Apr 10, 2011 at 07:36:49AM +0000, Jonathan Nieder wrote: > From: Jilles Tjoelker > Date: Sat, 13 Jun 2009 16:17:45 -0500 > > This change only affects strings passed to -c, when the -s option is > not used. > > Use the EV_EXIT flag to inform the eval machinery that the string > being passed is the entirety of input. This way, a fork may be > omitted in many special cases. > > If there are empty lines after the last command, the evalcmd will not > see the end early enough and forks will not be omitted. The same thing > seems to happen in bash. > > Example: > sh -c 'ps lT' > No longer shows a shell process waiting for ps to finish. > > [jn: ported from FreeBSD SVN r194128. Bugs are mine.] > > Signed-off-by: Jonathan Nieder Instead of detecting EOF using the input layer, I'm going to use the parser instead. In either case, we always have to read ahead in order to complete the parsing of the previous node. Therefore we always know whether there is more to come, except in the case where we see a newline/semicolon or similar. For the purposes of sh -c, this should be sufficient. Signed-off-by: Herbert Xu function old new delta evalstring 190 224 +34 ash_main 1014 1022 +8 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 42/0) Total: 42 bytes Signed-off-by: Denys Vlasenko --- shell/ash.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 42e14cbc8..c96ec939e 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -6128,7 +6128,9 @@ struct backcmd { /* result of evalbackcmd */ }; /* These forward decls are needed to use "eval" code for backticks handling: */ -#define EV_EXIT 01 /* exit after evaluating tree */ +/* flags in argument to evaltree */ +#define EV_EXIT 01 /* exit after evaluating tree */ +#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ static int evaltree(union node *, int); static void FAST_FUNC @@ -8345,10 +8347,6 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) static void *funcblock; /* block to allocate function from */ static char *funcstring_end; /* end of block to allocate strings from */ -/* flags in argument to evaltree */ -#define EV_EXIT 01 /* exit after evaluating tree */ -#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ - static const uint8_t nodesize[N_NUMBER] ALIGN1 = { [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)), @@ -12491,6 +12489,12 @@ expandstr(const char *ps) return stackblock(); } +static inline int +parser_eof(void) +{ + return tokpushback && lasttoken == TEOF; +} + /* * Execute a command or commands contained in a string. */ @@ -12526,7 +12530,7 @@ evalstring(char *s, int flags) while ((n = parsecmd(0)) != NODE_EOF) { int i; - i = evaltree(n, flags); + i = evaltree(n, flags & ~(parser_eof() ? 0 : EV_EXIT)); if (n) status = i; popstackmark(&smark); @@ -13671,7 +13675,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) // if (!sflag) g_parsefile->pf_fd = -1; // ^^ not necessary since now we special-case fd 0 // in is_hidden_fd() to not be considered "hidden fd" - evalstring(minusc, 0); + evalstring(minusc, sflag ? 0 : EV_EXIT); } if (sflag || minusc == NULL) { -- cgit v1.2.3-55-g6feb From 2093ad296f8a4528ad0e106b52074871a2bf070e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 26 Jul 2017 00:07:27 +0200 Subject: hush: fix ${##}, ${#?}, ${#!} handling function old new delta parse_dollar 786 820 +34 expand_one_var 1579 1592 +13 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 47/0) Total: 47 bytes Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-vars/param_expand_alt.tests | 2 +- shell/ash_test/ash-vars/param_expand_len1.right | 11 ++++++++ shell/ash_test/ash-vars/param_expand_len1.tests | 31 +++++++++++++++++++++++ shell/hush.c | 27 +++++++++++++++----- shell/hush_test/hush-vars/param_expand_alt.tests | 2 +- shell/hush_test/hush-vars/param_expand_len1.right | 11 ++++++++ shell/hush_test/hush-vars/param_expand_len1.tests | 31 +++++++++++++++++++++++ 7 files changed, 106 insertions(+), 9 deletions(-) create mode 100644 shell/ash_test/ash-vars/param_expand_len1.right create mode 100755 shell/ash_test/ash-vars/param_expand_len1.tests create mode 100644 shell/hush_test/hush-vars/param_expand_len1.right create mode 100755 shell/hush_test/hush-vars/param_expand_len1.tests diff --git a/shell/ash_test/ash-vars/param_expand_alt.tests b/shell/ash_test/ash-vars/param_expand_alt.tests index c9c4249af..d80452434 100755 --- a/shell/ash_test/ash-vars/param_expand_alt.tests +++ b/shell/ash_test/ash-vars/param_expand_alt.tests @@ -6,7 +6,7 @@ # now some funky ones. # ${V+word} "if V unset, then substitute nothing, else substitute word" # ${V:+word} "if V unset or '', then substitute nothing, else substitute word" -# bash doesn't accept ${#+}. ash prints 0 (not $#). +# bash doesn't accept ${#+}. ash prints 0 (not $#): "len of $+" echo _${#+}_ _${#:+}_ # Forms with non-empty word work as expected in both ash and bash. echo _${#+z}_ _${#:+z}_ 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 @@ +One:1 +Two:2 +Three:3 + +One:1 +Two:2 +Three:3 + +Ok ${#$}: 0 + +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 @@ +# ${#c} for any single char c means "length of $c", including all special vars + +false +echo One:${#?} +(exit 10) +echo Two:${#?} +(exit 100) +echo Three:${#?} + +echo +echo One:${##} +set -- 1 2 3 4 5 6 7 8 9 0 +echo Two:${##} +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 \ + 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 \ + 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 \ + 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 +echo Three:${##} + +echo +v=$$ +test "${#v}" = "${#$}" +echo 'Ok ${#$}:' $? + +echo +sleep 0 & +v=$! +test "${#v}" = "${#!}" +echo 'Ok ${#!}:' $? + +# TODO: ${#-} ${#_} diff --git a/shell/hush.c b/shell/hush.c index 11b33f40a..d0225edb9 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -4466,6 +4466,8 @@ static int parse_dollar(o_string *as_string, case '@': /* args */ goto make_one_char_var; case '{': { + char len_single_ch; + o_addchr(dest, SPECIAL_VAR_SYMBOL); ch = i_getch(input); /* eat '{' */ @@ -4485,6 +4487,7 @@ static int parse_dollar(o_string *as_string, return 0; } nommu_addchr(as_string, ch); + len_single_ch = ch; ch |= quote_mask; /* 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, /* handle parameter expansions * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 */ - if (!strchr(VAR_SUBST_OPS, ch)) /* ${var... */ - goto bad_dollar_syntax; - + if (!strchr(VAR_SUBST_OPS, ch)) { /* ${var... */ + if (len_single_ch != '#' + /*|| !strchr(SPECIAL_VARS_STR, ch) - disallow errors like ${#+} ? */ + || i_peek(input) != '}' + ) { + goto bad_dollar_syntax; + } + /* else: it's "length of C" ${#C} op, + * where C is a single char + * special var name, e.g. ${#!}. + */ + } /* Eat everything until closing '}' (or ':') */ end_ch = '}'; if (BASH_SUBSTR @@ -4568,6 +4580,7 @@ static int parse_dollar(o_string *as_string, } break; } + len_single_ch = 0; /* it can't be ${#C} op */ } o_addchr(dest, SPECIAL_VAR_SYMBOL); break; @@ -5559,10 +5572,10 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha first_char = arg[0] = arg0 & 0x7f; exp_op = 0; - if (first_char == '#' && arg[1] /* ${#... but not ${#} */ - && (!exp_saveptr /* and (not ${#...} */ - || (arg[1] == '?' && arg[2] == '\0') /* or ${#?} - "len of $?") */ - ) + if (first_char == '#' && arg[1] /* ${#...} but not ${#} */ + && (!exp_saveptr /* and ( not(${#...}) */ + || (arg[2] == '\0' && strchr(SPECIAL_VARS_STR, arg[1])) /* or ${#C} "len of $C" ) */ + ) /* NB: skipping ^^^specvar check mishandles ${#::2} */ ) { /* It must be length operator: ${#var} */ var++; diff --git a/shell/hush_test/hush-vars/param_expand_alt.tests b/shell/hush_test/hush-vars/param_expand_alt.tests index c9c4249af..d80452434 100755 --- a/shell/hush_test/hush-vars/param_expand_alt.tests +++ b/shell/hush_test/hush-vars/param_expand_alt.tests @@ -6,7 +6,7 @@ # now some funky ones. # ${V+word} "if V unset, then substitute nothing, else substitute word" # ${V:+word} "if V unset or '', then substitute nothing, else substitute word" -# bash doesn't accept ${#+}. ash prints 0 (not $#). +# bash doesn't accept ${#+}. ash prints 0 (not $#): "len of $+" echo _${#+}_ _${#:+}_ # Forms with non-empty word work as expected in both ash and bash. echo _${#+z}_ _${#:+z}_ 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 @@ +One:1 +Two:2 +Three:3 + +One:1 +Two:2 +Three:3 + +Ok ${#$}: 0 + +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 @@ +# ${#c} for any single char c means "length of $c", including all special vars + +false +echo One:${#?} +(exit 10) +echo Two:${#?} +(exit 100) +echo Three:${#?} + +echo +echo One:${##} +set -- 1 2 3 4 5 6 7 8 9 0 +echo Two:${##} +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 \ + 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 \ + 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 \ + 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 +echo Three:${##} + +echo +v=$$ +test "${#v}" = "${#$}" +echo 'Ok ${#$}:' $? + +echo +sleep 0 & +v=$! +test "${#v}" = "${#!}" +echo 'Ok ${#!}:' $? + +# TODO: ${#-} ${#_} -- cgit v1.2.3-55-g6feb From b0648b0e7874e8551df64708532346a049ab7f2c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 26 Jul 2017 00:30:02 +0200 Subject: shell: remove ${#+} tests, it is not a valid construct Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-vars/param_expand_alt.right | 2 +- shell/ash_test/ash-vars/param_expand_alt.tests | 9 +++++++-- shell/hush_test/hush-vars/param_expand_alt.right | 2 +- shell/hush_test/hush-vars/param_expand_alt.tests | 9 +++++++-- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/shell/ash_test/ash-vars/param_expand_alt.right b/shell/ash_test/ash-vars/param_expand_alt.right index c733c147a..1303f8064 100644 --- a/shell/ash_test/ash-vars/param_expand_alt.right +++ b/shell/ash_test/ash-vars/param_expand_alt.right @@ -1,6 +1,6 @@ SHELL: line 1: syntax error: bad substitution SHELL: line 1: syntax error: bad substitution -_0_ __ +__ _z_ _z_ _ _ _ _ _ _aaaa _ _ _word _word diff --git a/shell/ash_test/ash-vars/param_expand_alt.tests b/shell/ash_test/ash-vars/param_expand_alt.tests index d80452434..23e9a26be 100755 --- a/shell/ash_test/ash-vars/param_expand_alt.tests +++ b/shell/ash_test/ash-vars/param_expand_alt.tests @@ -6,8 +6,13 @@ # now some funky ones. # ${V+word} "if V unset, then substitute nothing, else substitute word" # ${V:+word} "if V unset or '', then substitute nothing, else substitute word" -# bash doesn't accept ${#+}. ash prints 0 (not $#): "len of $+" -echo _${#+}_ _${#:+}_ +# +# ${#:+} is a :+ op on $#, but ${#+} (and any other ${#c}) is "length of $c", +# not + op on $#. +# bash and dash do not accept ${#+}. it's possible for some shell to skip +# the check that c is valid and interpret ${#+} as "len of $+". Not testing it. +# echo _${#+}_ +echo _${#:+}_ # Forms with non-empty word work as expected in both ash and bash. echo _${#+z}_ _${#:+z}_ diff --git a/shell/hush_test/hush-vars/param_expand_alt.right b/shell/hush_test/hush-vars/param_expand_alt.right index 4f9eb2907..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,6 @@ hush: syntax error: unterminated ${name} hush: syntax error: unterminated ${name} -__ __ +__ _z_ _z_ _ _ _ _ _ _aaaa _ _ _word _word diff --git a/shell/hush_test/hush-vars/param_expand_alt.tests b/shell/hush_test/hush-vars/param_expand_alt.tests index d80452434..23e9a26be 100755 --- a/shell/hush_test/hush-vars/param_expand_alt.tests +++ b/shell/hush_test/hush-vars/param_expand_alt.tests @@ -6,8 +6,13 @@ # now some funky ones. # ${V+word} "if V unset, then substitute nothing, else substitute word" # ${V:+word} "if V unset or '', then substitute nothing, else substitute word" -# bash doesn't accept ${#+}. ash prints 0 (not $#): "len of $+" -echo _${#+}_ _${#:+}_ +# +# ${#:+} is a :+ op on $#, but ${#+} (and any other ${#c}) is "length of $c", +# not + op on $#. +# bash and dash do not accept ${#+}. it's possible for some shell to skip +# the check that c is valid and interpret ${#+} as "len of $+". Not testing it. +# echo _${#+}_ +echo _${#:+}_ # Forms with non-empty word work as expected in both ash and bash. echo _${#+z}_ _${#:+z}_ -- cgit v1.2.3-55-g6feb From b31b61bb9bff8a920b7e25b83e3aa08f7c907331 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 26 Jul 2017 13:42:53 +0200 Subject: ash: fix redir_leak.tests if STANDALONE=y If STANDALONE and we run a NOEXEC applet, saved copies of redirected fds were visible for the child. They have CLOEXEC bit, yes, but we do not exec in this case. Signed-off-by: Denys Vlasenko --- shell/ash.c | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/ash.c b/shell/ash.c index c96ec939e..524580e8a 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -7698,6 +7698,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char ** clearenv(); while (*envp) putenv(*envp++); + popredir(/*drop:*/ 1, /*restore:*/ 0); run_applet_no_and_exit(applet_no, cmd, argv); } /* re-exec ourselves with the new arguments */ -- cgit v1.2.3-55-g6feb From cf3a796dd1dfe53f7d67507f7f4d5c05cc7ebc8d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 26 Jul 2017 14:38:19 +0200 Subject: ash: alloc slightly smaller buffer in cvtnum(); faster unsetvar() Signed-off-by: Denys Vlasenko --- shell/ash.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 524580e8a..915e86167 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -214,6 +214,9 @@ #include "shell_common.h" #if ENABLE_FEATURE_SH_MATH # include "math.h" +#else +typedef long arith_t; +# define ARITH_FMT "%ld" #endif #if ENABLE_ASH_RANDOM_SUPPORT # include "random.h" @@ -621,8 +624,8 @@ fmtstr(char *outbuf, size_t length, const char *fmt, ...) va_list ap; int ret; - va_start(ap, fmt); INT_OFF; + va_start(ap, fmt); ret = vsnprintf(outbuf, length, fmt, ap); va_end(ap); INT_ON; @@ -2345,7 +2348,7 @@ setvar0(const char *name, const char *val) static void unsetvar(const char *s) { - setvar0(s, NULL); + setvar(s, NULL, 0); } /* @@ -5697,19 +5700,20 @@ static struct arglist exparg; /* * Our own itoa(). + * cvtnum() is used even if math support is off (to prepare $? values and such). */ -#if !ENABLE_FEATURE_SH_MATH -/* cvtnum() is used even if math support is off (to prepare $? values and such) */ -typedef long arith_t; -# define ARITH_FMT "%ld" -#endif static int cvtnum(arith_t num) { int len; - expdest = makestrspace(sizeof(arith_t)*3 + 2, expdest); - len = fmtstr(expdest, sizeof(arith_t)*3 + 2, ARITH_FMT, num); + /* 32-bit and wider ints require buffer size of bytes*3 (or less) */ + len = sizeof(arith_t) * 3; + /* If narrower: worst case, 1-byte ints: need 5 bytes: "-127" */ + if (sizeof(arith_t) < 4) len += 2; + + expdest = makestrspace(len, expdest); + len = fmtstr(expdest, len, ARITH_FMT, num); STADJUST(len, expdest); return len; } -- cgit v1.2.3-55-g6feb From b8ab27bf53797ec98c7c7d1c80a5a0a062f273a5 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 26 Jul 2017 19:22:34 +0200 Subject: ash: [VAR] Add localvars nesting Upstream commit: Date: Mon, 24 May 2010 15:31:27 +0800 [VAR] Add localvars nesting This patch adds localvars nesting infrastructure so we can reuse the localvars mechanism for command evaluation. Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 57 ++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 915e86167..8bef78546 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -9167,7 +9167,12 @@ optschanged(void) #endif } -static struct localvar *localvars; +struct localvar_list { + struct localvar_list *next; + struct localvar *lv; +}; + +static struct localvar_list *localvar_stack; /* * Called after a function returns. @@ -9176,11 +9181,19 @@ static struct localvar *localvars; static void poplocalvars(void) { - struct localvar *lvp; + struct localvar_list *ll; + struct localvar *lvp, *next; struct var *vp; - while ((lvp = localvars) != NULL) { - localvars = lvp->next; + INT_OFF; + ll = localvar_stack; + localvar_stack = ll->next; + + next = ll->lv; + free(ll); + + while ((lvp = next) != NULL) { + next = lvp->next; vp = lvp->vp; TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-")); if (vp == NULL) { /* $- saved */ @@ -9199,19 +9212,34 @@ poplocalvars(void) } free(lvp); } + INT_ON; +} + +/* + * Create a new localvar environment. + */ +static void +pushlocalvars(void) +{ + struct localvar_list *ll; + + INT_OFF; + ll = ckzalloc(sizeof(*ll)); + /*ll->lv = NULL; - zalloc did it */ + ll->next = localvar_stack; + localvar_stack = ll; + INT_ON; } static int evalfun(struct funcnode *func, int argc, char **argv, int flags) { volatile struct shparam saveparam; - struct localvar *volatile savelocalvars; struct jmploc *volatile savehandler; struct jmploc jmploc; int e; saveparam = shellparam; - savelocalvars = localvars; savehandler = exception_handler; e = setjmp(jmploc.loc); if (e) { @@ -9219,7 +9247,6 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) } INT_OFF; exception_handler = &jmploc; - localvars = NULL; shellparam.malloced = 0; func->count++; funcnest++; @@ -9230,13 +9257,13 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) shellparam.optind = 1; shellparam.optoff = -1; #endif + pushlocalvars(); evaltree(func->n.narg.next, flags & EV_TESTED); + poplocalvars(); funcdone: INT_OFF; funcnest--; freefunc(func); - poplocalvars(); - localvars = savelocalvars; freeparam(&shellparam); shellparam = saveparam; exception_handler = savehandler; @@ -9265,7 +9292,7 @@ mklocal(char *name) * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x */ - lvp = localvars; + lvp = localvar_stack->lv; while (lvp) { if (lvp->vp && varcmp(lvp->vp->var_text, name) == 0) { if (eq) @@ -9310,8 +9337,8 @@ mklocal(char *name) } } lvp->vp = vp; - lvp->next = localvars; - localvars = lvp; + lvp->next = localvar_stack->lv; + localvar_stack->lv = lvp; ret: INT_ON; } @@ -9324,7 +9351,7 @@ localcmd(int argc UNUSED_PARAM, char **argv) { char *name; - if (!funcnest) + if (!localvar_stack) ash_msg_and_raise_error("not in a function"); argv = argptr; @@ -13570,6 +13597,10 @@ reset(void) /* from redir.c: */ while (redirlist) popredir(/*drop:*/ 0, /*restore:*/ 0); + + /* from var.c: */ + while (localvar_stack) + poplocalvars(); } #if PROFILE -- cgit v1.2.3-55-g6feb From d5b500c81c1ec73d2feeea14c2e872726044d9e8 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 26 Jul 2017 19:25:40 +0200 Subject: ash: [VAR] Fix poplocalvar leak Upstream commit: Date: Tue, 25 May 2010 18:14:32 +0800 [VAR] Fix poplocalvar leak When a variable is marked as local, we set VSTRFIXED on its vp recored. However, poplocalvar never clears this flag for variables that were unset to begin with. Thus if you ever made an unset variable local, it would get the VSTRFIXED bit and stick around forever. Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/ash.c b/shell/ash.c index 8bef78546..75a72ea0c 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -9200,7 +9200,8 @@ poplocalvars(void) memcpy(optlist, lvp->text, sizeof(optlist)); free((char*)lvp->text); optschanged(); - } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { + } else if (lvp->flags == VUNSET) { + vp->flags &= ~(VSTRFIXED|VREADONLY); unsetvar(vp->var_text); } else { if (vp->var_func) -- cgit v1.2.3-55-g6feb From 981a0568b3f3003cd1a2640ade61d8f5ebdfb865 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 26 Jul 2017 19:53:11 +0200 Subject: ash: [VAR] Replace cmdenviron with localvars Upstream commit: Date: Wed, 26 May 2010 18:54:19 +0800 [VAR] Replace cmdenviron with localvars This patch replaces the cmdenviron mechanism for temporary command variables with the localvars mechanism used by functions. This reduces code size, and more importantly, makes the variable assignment take effect immediately as required by POSIX. Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 61 ++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 75a72ea0c..e900a425f 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -1250,7 +1250,6 @@ static struct parsefile basepf; /* top level input file */ static struct parsefile *g_parsefile = &basepf; /* current input file */ static int startlinno; /* line # where last token started */ static char *commandname; /* currently executing command */ -static struct strlist *cmdenviron; /* environment for builtin command */ /* ============ Message printing */ @@ -2225,15 +2224,9 @@ reinit_unicode_for_ash(void) /* * Search the environment of a builtin command. */ -static const char * +static inline const char * bltinlookup(const char *name) { - struct strlist *sp; - - for (sp = cmdenviron; sp; sp = sp->next) { - if (varcmp(sp->text, name) == 0) - return var_end(sp->text); - } return lookupvar(name); } @@ -9179,7 +9172,7 @@ static struct localvar_list *localvar_stack; * Interrupts must be off. */ static void -poplocalvars(void) +poplocalvars(int keep) { struct localvar_list *ll; struct localvar *lvp, *next; @@ -9196,7 +9189,23 @@ poplocalvars(void) next = lvp->next; vp = lvp->vp; TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-")); - if (vp == NULL) { /* $- saved */ + if (keep) { + int bits = VSTRFIXED; + + if (lvp->flags != VUNSET) { + if (vp->var_text == lvp->text) + bits |= VTEXTFIXED; + else if (!(lvp->flags & (VTEXTFIXED|VSTACK))) + free((char*)lvp->text); + } + + vp->flags &= ~bits; + vp->flags |= (lvp->flags & bits); + + if ((vp->flags & + (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET) + unsetvar(vp->var_text); + } else if (vp == NULL) { /* $- saved */ memcpy(optlist, lvp->text, sizeof(optlist)); free((char*)lvp->text); optschanged(); @@ -9260,7 +9269,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) #endif pushlocalvars(); evaltree(func->n.narg.next, flags & EV_TESTED); - poplocalvars(); + poplocalvars(0); funcdone: INT_OFF; funcnest--; @@ -9631,6 +9640,7 @@ evalcommand(union node *cmd, int flags) /* First expand the arguments. */ TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); setstackmark(&smark); + pushlocalvars(); back_exitstatus = 0; cmdentry.cmdtype = CMDBUILTIN; @@ -9684,6 +9694,8 @@ evalcommand(union node *cmd, int flags) spp = varlist.lastp; expandarg(argp, &varlist, EXP_VARTILDE); + mklocal((*spp)->text); + /* * Modify the command lookup path, if a PATH= assignment * is present @@ -9815,6 +9827,7 @@ evalcommand(union node *cmd, int flags) /* parent */ status = waitforjob(jp); INT_ON; + poplocalvars(0); TRACE(("forked child exited with %d\n", status)); break; } @@ -9827,17 +9840,10 @@ evalcommand(union node *cmd, int flags) /* NOTREACHED */ } /* default */ case CMDBUILTIN: - cmdenviron = varlist.list; - if (cmdenviron) { - struct strlist *list = cmdenviron; - int i = VNOSET; - if (spclbltin > 0 || argc == 0) { - i = 0; - if (cmd_is_exec && argc > 1) - i = VEXPORT; - } - listsetvar(list, i); - } + poplocalvars(spclbltin > 0 || argc == 0); + if (cmd_is_exec && argc > 1) + listsetvar(varlist.list, VEXPORT); + /* Tight loop with builtins only: * "while kill -0 $child; do true; done" * will never exit even if $child died, unless we do this @@ -9855,7 +9861,7 @@ evalcommand(union node *cmd, int flags) goto readstatus; case CMDFUNCTION: - listsetvar(varlist.list, 0); + poplocalvars(1); /* See above for the rationale */ dowait(DOWAIT_NONBLOCK, NULL); if (evalfun(cmdentry.u.func, argc, argv, flags)) @@ -12714,11 +12720,12 @@ dotcmd(int argc_ UNUSED_PARAM, char **argv_ UNUSED_PARAM) char *fullname; char **argv; char *args_need_save; - struct strlist *sp; volatile struct shparam saveparam; - for (sp = cmdenviron; sp; sp = sp->next) - setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED); +//??? +// struct strlist *sp; +// for (sp = cmdenviron; sp; sp = sp->next) +// setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED); nextopt(nullstr); /* handle possible "--" */ argv = argptr; @@ -13601,7 +13608,7 @@ reset(void) /* from var.c: */ while (localvar_stack) - poplocalvars(); + poplocalvars(0); } #if PROFILE -- cgit v1.2.3-55-g6feb From 484fc2056df8a82acabc70386eeb6d0da4982fec Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 26 Jul 2017 19:55:31 +0200 Subject: ash: [VAR] Fix poplocalvar on abnormal exit from function Upstream commit: Date: Thu, 27 May 2010 11:32:55 +0800 [VAR] Fix poplocalvar on abnormal exit from function The new localvar code broke the abnormal exit from functions and built-ins by not restoring the original localvar state. This patch fixes this by storing the previous localvar state so that we always unwind correctly in case of an abnormal exit. Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index e900a425f..f7fc18f0d 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -9228,7 +9228,7 @@ poplocalvars(int keep) /* * Create a new localvar environment. */ -static void +static struct localvar_list * pushlocalvars(void) { struct localvar_list *ll; @@ -9239,6 +9239,15 @@ pushlocalvars(void) ll->next = localvar_stack; localvar_stack = ll; INT_ON; + + return ll->next; +} + +static void +unwindlocalvars(struct localvar_list *stop) +{ + while (localvar_stack != stop) + poplocalvars(0); } static int @@ -9619,6 +9628,7 @@ evalcommand(union node *cmd, int flags) static const struct builtincmd null_bltin = { "\0\0", bltincmd /* why three NULs? */ }; + struct localvar_list *localvar_stop; struct stackmark smark; union node *argp; struct arglist arglist; @@ -9640,7 +9650,7 @@ evalcommand(union node *cmd, int flags) /* First expand the arguments. */ TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); setstackmark(&smark); - pushlocalvars(); + localvar_stop = pushlocalvars(); back_exitstatus = 0; cmdentry.cmdtype = CMDBUILTIN; @@ -9827,7 +9837,6 @@ evalcommand(union node *cmd, int flags) /* parent */ status = waitforjob(jp); INT_ON; - poplocalvars(0); TRACE(("forked child exited with %d\n", status)); break; } @@ -9874,6 +9883,7 @@ evalcommand(union node *cmd, int flags) out: if (cmd->ncmd.redirect) popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec); + unwindlocalvars(localvar_stop); if (lastarg) { /* dsl: I think this is intended to be used to support * '_' in 'vi' command mode during line editing... @@ -13607,8 +13617,7 @@ reset(void) popredir(/*drop:*/ 0, /*restore:*/ 0); /* from var.c: */ - while (localvar_stack) - poplocalvars(0); + unwindlocalvars(NULL); } #if PROFILE -- cgit v1.2.3-55-g6feb From 85241c7b0b4558f405f7e633ad62520299b8396d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 26 Jul 2017 20:00:08 +0200 Subject: ash: [VAR] Do not poplocalvars prematurely on regular utilities Upstream commit: Date: Thu, 27 May 2010 11:50:19 +0800 [VAR] Do not poplocalvars prematurely on regular utilities The recent cmdenviron removal broke regular utilities by calling poplocalvars too early. This patch fixes that by postponing the poplocalvars for regular utilities until they have completed. In order to ensure that local still works, it is now a special built-in. Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index f7fc18f0d..3604af4fc 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -9539,7 +9539,7 @@ static const struct builtincmd builtintab[] = { #if ENABLE_FEATURE_SH_MATH { BUILTIN_NOSPEC "let" , letcmd }, #endif - { BUILTIN_ASSIGN "local" , localcmd }, + { BUILTIN_SPEC_REG_ASSG "local" , localcmd }, #if ENABLE_ASH_PRINTF { BUILTIN_REGULAR "printf" , printfcmd }, #endif @@ -9849,9 +9849,11 @@ evalcommand(union node *cmd, int flags) /* NOTREACHED */ } /* default */ case CMDBUILTIN: - poplocalvars(spclbltin > 0 || argc == 0); - if (cmd_is_exec && argc > 1) - listsetvar(varlist.list, VEXPORT); + if (spclbltin > 0 || argc == 0) { + poplocalvars(1); + if (cmd_is_exec && argc > 1) + listsetvar(varlist.list, VEXPORT); + } /* Tight loop with builtins only: * "while kill -0 $child; do true; done" -- cgit v1.2.3-55-g6feb From d04fc712e30b681117afaad490fec2a747b390c6 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 26 Jul 2017 20:06:48 +0200 Subject: ash: [VAR] Fix loss of variables when hash collides Upstream commit: Date: Tue, 6 Jul 2010 17:40:53 +0800 [VAR] Fix loss of variables when hash collides Brian Koropoff reported that the new var patches broke the following script: #!/bin/dash GDM_LANG="bar" OPTION="foo" unset GDM_LANG # OPTION has mysteriously become unset echo "$OPTION" He correctly diagnosed this as a result of removing all variables in the hash chain preceding the one that should be removed in setvareq. He also provided a patch to fix this. This patch is based on his but without keeping the original vpp. As a result, we now store new variables at the end of the hash chain instead of the beginning. To make this work, setvareq/setvar now returns the vp pointer modified. In case they're used to unset a variable the pointer returned is undefined. This is because mklocal needs it and used to get it by assuming that the new variable always appear at the beginning of the chain. Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 3604af4fc..5bb59355c 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -2237,14 +2237,15 @@ bltinlookup(const char *name) * will go away. * Called with interrupts off. */ -static void +static struct var * setvareq(char *s, int flags) { struct var *vp, **vpp; vpp = hashvar(s); flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); - vp = *findvar(vpp, s); + vpp = findvar(vpp, s); + vp = *vpp; if (vp) { if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) { const char *n; @@ -2257,7 +2258,7 @@ setvareq(char *s, int flags) } if (flags & VNOSET) - return; + goto out; if (vp->var_func && !(flags & VNOFUNC)) vp->var_func(var_end(s)); @@ -2271,14 +2272,14 @@ setvareq(char *s, int flags) out_free: if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE) free(s); - return; + goto out; } flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET); } else { /* variable s is not found */ if (flags & VNOSET) - return; + goto out; if ((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET) goto out_free; vp = ckzalloc(sizeof(*vp)); @@ -2290,13 +2291,16 @@ setvareq(char *s, int flags) s = ckstrdup(s); vp->var_text = s; vp->flags = flags; + + out: + return vp; } /* * Set the value of a variable. The flags argument is ored with the * flags of the variable. If val is NULL, the variable is unset. */ -static void +static struct var * setvar(const char *name, const char *val, int flags) { const char *q; @@ -2304,6 +2308,7 @@ setvar(const char *name, const char *val, int flags) char *nameeq; size_t namelen; size_t vallen; + struct var *vp; q = endofname(name); p = strchrnul(q, '='); @@ -2325,8 +2330,10 @@ setvar(const char *name, const char *val, int flags) p = mempcpy(p, val, vallen); } *p = '\0'; - setvareq(nameeq, flags | VNOSAVE); + vp = setvareq(nameeq, flags | VNOSAVE); INT_ON; + + return vp; } static void FAST_FUNC @@ -9336,10 +9343,9 @@ mklocal(char *name) if (vp == NULL) { /* variable did not exist yet */ if (eq) - setvareq(name, VSTRFIXED); + vp = setvareq(name, VSTRFIXED); else - setvar(name, NULL, VSTRFIXED); - vp = *vpp; /* the new variable */ + vp = setvar(name, NULL, VSTRFIXED); lvp->flags = VUNSET; } else { lvp->text = vp->var_text; -- cgit v1.2.3-55-g6feb From b8c0bc18f0cc31e3a2f41dd2c0b30426e4a77fc5 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 26 Jul 2017 23:03:21 +0200 Subject: ash: revert previous implementation of "A=1 A=2 B=$A cmd" code Reverts this: commit 0e6f661e23d358cca104c24f8438d0ec64df32f1 Date: Fri Feb 15 15:02:15 2008 +0000 ash: handle "A=1 A=2 B=$A; echo $B". closes bug 947. A different fix from upstream has been imported by previous six commits. Last seven commits, cumulative: function old new delta poplocalvars - 314 +314 mklocal - 288 +288 pushlocalvars - 48 +48 evalcommand 1372 1408 +36 unwindlocalvars - 22 +22 ash_main 1022 1029 +7 setvar 167 172 +5 localvar_stack - 4 +4 setvareq 303 302 -1 evalcase 271 269 -2 subevalvar 1202 1198 -4 localvars 4 - -4 cmdenviron 4 - -4 expandarg 984 973 -11 evalvar 589 574 -15 argstr 1164 1141 -23 dotcmd 335 303 -32 bltinlookup 51 5 -46 varvalue 709 596 -113 evalfun 456 270 -186 localcmd 364 44 -320 ------------------------------------------------------------------------------ (add/remove: 5/2 grow/shrink: 3/11 up/down: 724/-761) Total: -37 bytes text data bss dec hex filename 915353 485 6888 922726 e1466 busybox_old 915320 485 6880 922685 e143d busybox_unstripped Signed-off-by: Denys Vlasenko --- shell/ash.c | 62 +++++++++++++++---------------------------------------------- 1 file changed, 15 insertions(+), 47 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 5bb59355c..faa42e28c 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -6304,19 +6304,15 @@ expari(int flag) #endif /* argstr needs it */ -static char *evalvar(char *p, int flags, struct strlist *var_str_list); +static char *evalvar(char *p, int flags); /* * Perform variable and command substitution. If EXP_FULL is set, output CTLESC * characters to allow for further processing. Otherwise treat * $@ like $* since no splitting will be performed. - * - * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence - * over shell variables. Needed for "A=a B=$A; echo $B" case - we use it - * for correct expansion of "B=$A" word. */ static void -argstr(char *p, int flags, struct strlist *var_str_list) +argstr(char *p, int flags) { static const char spclchars[] ALIGN1 = { '=', @@ -6409,7 +6405,7 @@ argstr(char *p, int flags, struct strlist *var_str_list) inquotes ^= EXP_QUOTED; /* "$@" syntax adherence hack */ if (inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) { - p = evalvar(p + 1, flags | inquotes, /* var_str_list: */ NULL) + 1; + p = evalvar(p + 1, flags | inquotes) + 1; goto start; } addquote: @@ -6435,7 +6431,7 @@ argstr(char *p, int flags, struct strlist *var_str_list) goto addquote; case CTLVAR: TRACE(("argstr: evalvar('%s')\n", p)); - p = evalvar(p, flags | inquotes, var_str_list); + p = evalvar(p, flags | inquotes); TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock())); goto start; case CTLBACKQ: @@ -6577,7 +6573,7 @@ varunset(const char *end, const char *var, const char *umsg, int varflags) static const char * subevalvar(char *p, char *varname, int strloc, int subtype, - int startloc, int varflags, int flag, struct strlist *var_str_list) + int startloc, int varflags, int flag) { struct nodelist *saveargbackq = argbackq; int quotes = flag & QUOTES_ESC; @@ -6595,8 +6591,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype, // p, varname, strloc, subtype, startloc, varflags, quotes); argstr(p, EXP_TILDE | (subtype != VSASSIGN && subtype != VSQUESTION ? - (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE) : 0), - var_str_list); + (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE) : 0) + ); STPUTC('\0', expdest); argbackq = saveargbackq; startp = (char *)stackblock() + startloc; @@ -6873,7 +6869,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype, * ash -c 'echo ${#1#}' name:'1=#' */ static NOINLINE ssize_t -varvalue(char *name, int varflags, int flags, struct strlist *var_str_list, int *quotedp) +varvalue(char *name, int varflags, int flags, int *quotedp) { const char *p; int num; @@ -6965,31 +6961,6 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list, int goto value; default: /* NB: name has form "VAR=..." */ - - /* "A=a B=$A" case: var_str_list is a list of "A=a" strings - * which should be considered before we check variables. */ - if (var_str_list) { - unsigned name_len = (strchrnul(name, '=') - name) + 1; - p = NULL; - do { - char *str, *eq; - str = var_str_list->text; - eq = strchr(str, '='); - if (!eq) /* stop at first non-assignment */ - break; - eq++; - if (name_len == (unsigned)(eq - str) - && strncmp(str, name, name_len) == 0 - ) { - p = eq; - /* goto value; - WRONG! */ - /* think "A=1 A=2 B=$A" */ - } - var_str_list = var_str_list->next; - } while (var_str_list); - if (p) - goto value; - } p = lookupvar(name); value: if (!p) @@ -7019,7 +6990,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list, int * input string. */ static char * -evalvar(char *p, int flag, struct strlist *var_str_list) +evalvar(char *p, int flag) { char varflags; char subtype; @@ -7043,7 +7014,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list) p = strchr(p, '=') + 1; //TODO: use var_end(p)? again: - varlen = varvalue(var, varflags, flag, var_str_list, "ed); + varlen = varvalue(var, varflags, flag, "ed); if (varflags & VSNUL) varlen--; @@ -7057,8 +7028,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list) if (varlen < 0) { argstr( p, - flag | EXP_TILDE | EXP_WORD, - var_str_list + flag | EXP_TILDE | EXP_WORD ); goto end; } @@ -7070,7 +7040,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list) goto record; subevalvar(p, var, 0, subtype, startloc, varflags, - flag & ~QUOTES_ESC, var_str_list); + flag & ~QUOTES_ESC); varflags &= ~VSNUL; /* * Remove any recorded regions beyond @@ -7123,7 +7093,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list) STPUTC('\0', expdest); patloc = expdest - (char *)stackblock(); if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype, - startloc, varflags, flag, var_str_list)) { + startloc, varflags, flag)) { int amount = expdest - ( (char *)stackblock() + patloc - 1 ); @@ -7547,8 +7517,7 @@ expandarg(union node *arg, struct arglist *arglist, int flag) argbackq = arg->narg.backquote; STARTSTACKSTR(expdest); TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag)); - argstr(arg->narg.text, flag, - /* var_str_list: */ arglist ? arglist->list : NULL); + argstr(arg->narg.text, flag); p = _STPUTC('\0', expdest); expdest = p - 1; if (arglist == NULL) { @@ -7615,8 +7584,7 @@ casematch(union node *pattern, char *val) setstackmark(&smark); argbackq = pattern->narg.backquote; STARTSTACKSTR(expdest); - argstr(pattern->narg.text, EXP_TILDE | EXP_CASE, - /* var_str_list: */ NULL); + argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); STACKSTRNUL(expdest); ifsfree(); result = patmatch(stackblock(), val); -- cgit v1.2.3-55-g6feb From 488e609203c23b9826f75179f1b8e567617138ae Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 26 Jul 2017 23:08:36 +0200 Subject: ash: force inlining of a trivial function function old new delta bltinlookup 5 - -5 Signed-off-by: Denys Vlasenko --- shell/ash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/ash.c b/shell/ash.c index faa42e28c..f74fbd72f 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -2224,7 +2224,7 @@ reinit_unicode_for_ash(void) /* * Search the environment of a builtin command. */ -static inline const char * +static ALWAYS_INLINE const char * bltinlookup(const char *name) { return lookupvar(name); -- cgit v1.2.3-55-g6feb From 86d5bf4246a7ba68d220bc6c7a7a3be62119dc12 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 27 Jul 2017 02:59:13 +0200 Subject: config: trim/improve item names and help texts. Signed-off-by: Denys Vlasenko --- Config.in | 96 +++++++++++++++++++++++++++----------------------------- libbb/Config.src | 5 ++- 2 files changed, 48 insertions(+), 53 deletions(-) diff --git a/Config.in b/Config.in index c1cc591f3..acd3cb883 100644 --- a/Config.in +++ b/Config.in @@ -3,13 +3,13 @@ # see scripts/kbuild/config-language.txt. # -mainmenu "BusyBox Configuration" +mainmenu "Configuration" config HAVE_DOT_CONFIG bool default y -menu "Busybox Settings" +menu "Settings" config DESKTOP bool "Enable compatibility for full-blown desktop systems" @@ -57,7 +57,7 @@ config SHOW_USAGE bool "Show applet usage messages" default y help - Enabling this option, BusyBox applets will show terse help messages + Enabling this option, applets will show terse help messages when invoked with wrong arguments. If you do not want to show any (helpful) usage message when issuing wrong command syntax, you can say 'N' here, @@ -68,18 +68,16 @@ config FEATURE_VERBOSE_USAGE default y depends on SHOW_USAGE help - All BusyBox applets will show verbose help messages when - busybox is invoked with --help. This will add a lot of text to the - busybox binary. In the default configuration, this will add about - 13k, but it can add much more depending on your configuration. + All applets will show verbose help messages when invoked with --help. + This will add a lot of text to the binary. config FEATURE_COMPRESS_USAGE bool "Store applet usage messages in compressed form" default y depends on SHOW_USAGE help - Store usage messages in .bz compressed form, uncompress them - on-the-fly when --help is called. + Store usage messages in .bz2 compressed form, uncompress them + on-the-fly when "APPLET --help" is run. If you have a really tiny busybox with few applets enabled (and bunzip2 isn't one of them), the overhead of the decompressor might @@ -91,25 +89,24 @@ config LFS bool "Support files > 2 GB" default y help - If you want to build BusyBox with large file support, then enable - this option. This will have no effect if your kernel or your C + If you need to work with large files, enable this option. + This will have no effect if your kernel or your C library lacks large file support for large files. Some of the programs that can benefit from large file support include dd, gzip, - cp, mount, tar, and many others. If you want to access files larger - than 2 Gigabytes, enable this option. + cp, mount, tar. config PAM bool "Support PAM (Pluggable Authentication Modules)" default n help - Use PAM in some busybox applets (currently login and httpd) instead + Use PAM in some applets (currently login and httpd) instead of direct access to password database. config FEATURE_DEVPTS bool "Use the devpts filesystem for Unix98 PTYs" default y help - Enable if you want BusyBox to use Unix98 PTY support. If enabled, + Enable if you want to use Unix98 PTY support. If enabled, busybox will use /dev/ptmx for the master side of the pseudoterminal and /dev/pts/ for the slave side. Otherwise, BSD style /dev/ttyp will be used. To use this option, you should have @@ -157,9 +154,9 @@ config BUSYBOX bool "Include busybox applet" default y help - The busybox applet provides general help regarding busybox and - allows the included applets to be listed. It's also required - if applet links are to be installed at runtime. If you unselect + The busybox applet provides general help message and allows + the included applets to be listed. It also provides + optional --install command to create applet links. If you unselect this option, running busybox without any arguments will give just a cryptic error message: @@ -181,7 +178,7 @@ config INSTALL_NO_USR bool "Don't use /usr" default n help - Disable use of /usr. busybox --install and "make install" + Disable use of /usr. "busybox --install" and "make install" will install applets only to /bin and /sbin, never to /usr/bin or /usr/sbin. @@ -194,11 +191,11 @@ config FEATURE_SUID root-level operations even when run by ordinary users (for example, mounting of user mounts in fstab needs this). - With this option enabled, Busybox drops privileges for applets + With this option enabled, busybox drops privileges for applets that don't need root access, before entering their main() function. If you are really paranoid and don't want even initial busybox code - to run under root for evey applet, build two busybox binaries with + to run under root for every applet, build two busybox binaries with different applets in them (and the appropriate symlinks pointing to each binary), and only set the suid bit on the one that needs it. @@ -284,14 +281,14 @@ config FEATURE_PREFER_APPLETS (command name can be shown as 'exe' for applets started this way). config BUSYBOX_EXEC_PATH - string "Path to BusyBox executable" + string "Path to busybox executable" default "/proc/self/exe" help - When Busybox applets need to run other busybox applets, BusyBox + When applets need to run other applets, busybox sometimes needs to exec() itself. When the /proc filesystem is mounted, /proc/self/exe always points to the currently running executable. If you haven't got /proc, set this to wherever you - want to run BusyBox from. + want to run busybox from. config SELINUX bool "Support NSA Security Enhanced Linux" @@ -354,21 +351,17 @@ config PLATFORM_LINUX comment 'Build Options' config STATIC - bool "Build BusyBox as a static binary (no shared libs)" + bool "Build static binary (no shared libs)" default n help - If you want to build a static BusyBox binary, which does not - use or require any shared libraries, then enable this option. - This can cause BusyBox to be considerably larger, so you should - leave this option false unless you have a good reason (i.e. - your target platform does not support shared libraries, or - you are building an initrd which doesn't need anything but - BusyBox, etc). - - Most people will leave this set to 'N'. + If you want to build a static binary, which does not use + or require any shared libraries, enable this option. + Static binaries are larger, but do not require functioning + dynamic libraries to be present, which is important if used + as a system rescue tool. config PIE - bool "Build BusyBox as a position independent executable" + bool "Build position independent executable" default n depends on !STATIC help @@ -466,10 +459,10 @@ config FEATURE_SHARED_BUSYBOX ### Say 'N' unless you know what you are doing. config CROSS_COMPILER_PREFIX - string "Cross Compiler prefix" + string "Cross compiler prefix" default "" help - If you want to build BusyBox with a cross compiler, then you + If you want to build busybox with a cross compiler, then you will need to set this to the cross-compiler prefix, for example, "i386-uclibc-". @@ -482,11 +475,11 @@ config SYSROOT string "Path to sysroot" default "" help - If you want to build BusyBox with a cross compiler, then you + If you want to build busybox with a cross compiler, then you might also need to specify where /usr/include and /usr/lib will be found. - For example, BusyBox can be built against an installed + For example, busybox can be built against an installed Android NDK, platform version 9, for ARM ABI with CONFIG_SYSROOT=/opt/android-ndk/platforms/android-9/arch-arm @@ -580,21 +573,22 @@ config INSTALL_SH_APPLET_SCRIPT_WRAPPER endchoice config PREFIX - string "BusyBox installation prefix" + string "Destination path for 'make install'" default "./_install" help - Define your directory to install BusyBox files/subdirs in. + Where "make install" should install busybox binary and links. comment 'Debugging Options' config DEBUG - bool "Build BusyBox with extra Debugging symbols" + bool "Build with debug information" default n help - Say Y here if you wish to examine BusyBox internals while applets are - running. This increases the size of the binary considerably, and - should only be used when doing development. If you are doing - development and want to debug BusyBox, answer Y. + Say Y here to compile with debug information. + This increases the size of the binary considerably, and + should only be used when doing development. + + This adds -g option to gcc command line. Most people should answer N. @@ -609,6 +603,8 @@ config DEBUG_PESSIMIZE in a much bigger executable that more closely matches the source code. + This replaces -Os/-O2 with -O0 in gcc command line. + config DEBUG_SANITIZE bool "Enable runtime sanitizers (ASAN/LSAN/USAN/etc...)" default n @@ -626,7 +622,7 @@ config UNIT_TEST default n help Say Y here if you want to build unit tests (both the framework and - test cases) as a Busybox applet. This results in bigger code, so you + test cases) as an applet. This results in bigger code, so you probably don't want this option in production builds. config WERROR @@ -641,8 +637,8 @@ choice prompt "Additional debugging library" default NO_DEBUG_LIB help - Using an additional debugging library will make BusyBox become - considerable larger and will cause it to run more slowly. You + Using an additional debugging library will make busybox become + considerably larger and will cause it to run more slowly. You should always leave this option disabled for production use. dmalloc support: @@ -663,7 +659,7 @@ choice This enables compiling with Electric-fence support. Electric fence is another very useful malloc debugging library which uses your computer's virtual memory hardware to detect illegal memory - accesses. This support will make BusyBox be considerable larger + accesses. This support will make busybox be considerably larger and run slower, so you should leave this option disabled unless you are hunting a hard to find memory problem. 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 prompt "Buffer allocation policy" default FEATURE_BUFFERS_USE_MALLOC help - There are 3 ways BusyBox can handle buffer allocations: + There are 3 ways busybox can handle buffer allocations: - Use malloc. This costs code size for the call to xmalloc. - Put them on stack. For some very small machines with limited stack space, this can be deadly. For most folks, this works just fine. - Put them in BSS. This works beautifully for computers with a real MMU (and OS support), but wastes runtime RAM for uCLinux. This - behavior was the only one available for BusyBox versions 0.48 and - earlier. + behavior was the only one available for versions 0.48 and earlier. config FEATURE_BUFFERS_USE_MALLOC bool "Allocate with Malloc" -- cgit v1.2.3-55-g6feb From 68b653b66b0db6b1554806650fb0bebd7af9ef3b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 27 Jul 2017 10:53:09 +0200 Subject: config: trim/improve item names and help texts, take 2 Signed-off-by: Denys Vlasenko --- editors/awk.c | 3 +- loginutils/login.c | 2 +- loginutils/passwd.c | 2 +- loginutils/vlock.c | 2 +- miscutils/crontab.c | 2 +- networking/ftpd.c | 13 ++++++-- networking/ifupdown.c | 4 +-- networking/ip.c | 18 +++++----- networking/telnetd.c | 2 +- networking/tftp.c | 16 ++++----- networking/udhcp/Config.src | 80 ++++++++++++++++++++++++--------------------- networking/udhcp/d6_dhcpc.c | 2 +- util-linux/blkid.c | 2 -- util-linux/findfs.c | 2 -- util-linux/mdev.c | 2 +- util-linux/mount.c | 3 +- 16 files changed, 81 insertions(+), 74 deletions(-) diff --git a/editors/awk.c b/editors/awk.c index aa927db1a..cc17ad438 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -11,8 +11,7 @@ //config: bool "awk (22 kb)" //config: default y //config: help -//config: Awk is used as a pattern scanning and processing language. This is -//config: the BusyBox implementation of that programming language. +//config: Awk is used as a pattern scanning and processing language. //config: //config:config FEATURE_AWK_LIBM //config: bool "Enable math functions (requires libm)" 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 @@ //config: help //config: login is used when signing onto a system. //config: -//config: Note that Busybox binary must be setuid root for this applet to +//config: Note that busybox binary must be setuid root for this applet to //config: work properly. //config: //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 @@ //config: may change the password for any account. The administrator of a group //config: may change the password for the group. //config: -//config: Note that Busybox binary must be setuid root for this applet to +//config: Note that busybox binary must be setuid root for this applet to //config: work properly. //config: //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 @@ //config: help //config: Build the "vlock" applet which allows you to lock (virtual) terminals. //config: -//config: Note that Busybox binary must be setuid root for this applet to +//config: Note that busybox binary must be setuid root for this applet to //config: work properly. //applet:/* Needs to be run by root or be suid root - needs to change uid and gid: */ 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 @@ //config: help //config: Crontab manipulates the crontab for a particular user. Only //config: the superuser may specify a different user and/or crontab directory. -//config: Note that Busybox binary must be setuid root for this applet to +//config: Note that busybox binary must be setuid root for this applet to //config: work properly. /* 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 @@ //config: Simple FTP daemon. You have to run it via inetd. //config: //config:config FEATURE_FTPD_WRITE -//config: bool "Enable upload commands" +//config: bool "Enable -w (upload commands)" //config: default y //config: depends on FTPD //config: help -//config: Enable all kinds of FTP upload commands (-w option) +//config: Enable -w option. "ftpd -w" will accept upload commands +//config: such as STOR, STOU, APPE, DELE, MKD, RMD, rename commands. //config: //config:config FEATURE_FTPD_ACCEPT_BROKEN_LIST //config: bool "Enable workaround for RFC-violating clients" @@ -40,7 +41,13 @@ //config: default y //config: depends on FTPD //config: help -//config: Enable basic system login as seen in telnet etc. +//config: Require login, and change to logged in user's UID:GID before +//config: accessing any files. Option "-a USER" allows "anonymous" +//config: logins (treats them as if USER logged in). +//config: +//config: If this option is not selected, ftpd runs with the rights +//config: of the user it was started under, and does not require login. +//config: Take care to not launch it under root. //applet:IF_FTPD(APPLET(ftpd, BB_DIR_USR_SBIN, BB_SUID_DROP)) 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 @@ //config: than the default of using the older "ifconfig" and "route" utilities. //config: //config: If Y: you must install either the full-blown iproute2 package -//config: or enable "ip" applet in Busybox, or the "ifup" and "ifdown" applets +//config: or enable "ip" applet in busybox, or the "ifup" and "ifdown" applets //config: will not work. //config: //config: If N: you must install either the full-blown ifconfig and route -//config: utilities, or enable these applets in Busybox. +//config: utilities, or enable these applets in busybox. //config: //config:config FEATURE_IFUPDOWN_IPV4 //config: bool "Support IPv4" 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 @@ //config: select PLATFORM_LINUX //config: help //config: The "ip" applet is a TCP/IP interface configuration and routing -//config: utility. You generally don't need "ip" to use busybox with -//config: TCP/IP. +//config: utility. +//config: Short forms (enabled below) are busybox-specific extensions. +//config: The standard "ip" utility does not provide them. If you are +//config: trying to be portable, it's better to use "ip CMD" forms. //config: //config:config IPADDR //config: bool "ipaddr (14 kb)" @@ -23,7 +25,7 @@ //config: select FEATURE_IP_ADDRESS //config: select PLATFORM_LINUX //config: help -//config: Support short form of ip addr: ipaddr +//config: Short form of "ip addr" //config: //config:config IPLINK //config: bool "iplink (16 kb)" @@ -31,7 +33,7 @@ //config: select FEATURE_IP_LINK //config: select PLATFORM_LINUX //config: help -//config: Support short form of ip link: iplink +//config: Short form of "ip link" //config: //config:config IPROUTE //config: bool "iproute (15 kb)" @@ -39,7 +41,7 @@ //config: select FEATURE_IP_ROUTE //config: select PLATFORM_LINUX //config: help -//config: Support short form of ip route: iproute +//config: Short form of "ip route" //config: //config:config IPTUNNEL //config: bool "iptunnel (9.6 kb)" @@ -47,7 +49,7 @@ //config: select FEATURE_IP_TUNNEL //config: select PLATFORM_LINUX //config: help -//config: Support short form of ip tunnel: iptunnel +//config: Short form of "ip tunnel" //config: //config:config IPRULE //config: bool "iprule (10 kb)" @@ -55,7 +57,7 @@ //config: select FEATURE_IP_RULE //config: select PLATFORM_LINUX //config: help -//config: Support short form of ip rule: iprule +//config: Short form of "ip rule" //config: //config:config IPNEIGH //config: bool "ipneigh (8.3 kb)" @@ -63,7 +65,7 @@ //config: select FEATURE_IP_NEIGH //config: select PLATFORM_LINUX //config: help -//config: Support short form of ip neigh: ipneigh +//config: Short form of "ip neigh" //config: //config:config FEATURE_IP_ADDRESS //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 @@ //config: //config: You need to be sure that busybox has LOGIN and //config: FEATURE_SUID enabled. And finally, you should make -//config: certain that Busybox has been installed setuid root: +//config: certain that busybox has been installed setuid root: //config: //config: chown root.root /bin/busybox //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 @@ //config: bool "tftp (12 kb)" //config: default y //config: help -//config: This enables the Trivial File Transfer Protocol client program. TFTP -//config: is usually used for simple, small transfers such as a root image +//config: Trivial File Transfer Protocol client. TFTP is usually used +//config: for simple, small transfers such as a root image //config: for a network-enabled bootloader. //config: +//config:config FEATURE_TFTP_PROGRESS_BAR +//config: bool "Enable progress bar" +//config: default y +//config: depends on TFTP +//config: //config:config TFTPD //config: bool "tftpd (10 kb)" //config: default y //config: help -//config: This enables the Trivial File Transfer Protocol server program. +//config: Trivial File Transfer Protocol server. //config: It expects that stdin is a datagram socket and a packet //config: is already pending on it. It will exit after one transfer. //config: In other words: it should be run from inetd in nowait mode, @@ -68,11 +73,6 @@ //config: Allow tftp to specify block size, and tftpd to understand //config: "blksize" and "tsize" options. //config: -//config:config FEATURE_TFTP_PROGRESS_BAR -//config: bool "Enable progress bar" -//config: default y -//config: depends on TFTP && FEATURE_TFTP_BLOCKSIZE -//config: //config:config TFTP_DEBUG //config: bool "Enable debug" //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 @@ # see scripts/kbuild/config-language.txt. # -INSERT - config UDHCPD - bool "udhcpd (DHCP server)" + bool "udhcpd" default y select PLATFORM_LINUX help udhcpd is a DHCP server geared primarily toward embedded systems, while striving to be fully functional and RFC compliant. -config FEATURE_UDHCPD_WRITE_LEASES_EARLY - bool "Rewrite the lease file at every new acknowledge" - default y - depends on UDHCPD - help - If selected, udhcpd will write a new file with leases every - time a new lease has been accepted, thus eliminating the need - to send SIGUSR1 for the initial writing or updating. Any timed - rewriting remains undisturbed. - config FEATURE_UDHCPD_BASE_IP_ON_MAC bool "Select IP address based on client MAC" default n @@ -37,6 +25,16 @@ config FEATURE_UDHCPD_BASE_IP_ON_MAC for the same client to (almost always) contain the same IP address. +config FEATURE_UDHCPD_WRITE_LEASES_EARLY + bool "Rewrite lease file at every new acknowledge" + default y + depends on UDHCPD + help + If selected, udhcpd will write a new file with leases every + time a new lease has been accepted, thus eliminating the need + to send SIGUSR1 for the initial writing or updating. Any timed + rewriting remains undisturbed. + config DHCPD_LEASES_FILE string "Absolute path to lease file" default "/var/lib/misc/udhcpd.leases" @@ -57,12 +55,12 @@ config DHCPRELAY bool "dhcprelay (5.8 kb)" default y help - dhcprelay listens for dhcp requests on one or more interfaces - and forwards these requests to a different interface or dhcp + dhcprelay listens for DHCP requests on one or more interfaces + and forwards these requests to a different interface or DHCP server. config UDHCPC - bool "udhcpc (DHCP client)" + bool "udhcpc" default y select PLATFORM_LINUX help @@ -102,19 +100,25 @@ config UDHCPC_DEFAULT_SCRIPT examples/udhcp for a working example. Normally it is safe to leave this untouched. +# udhcpc6 config is inserted here: +INSERT + +comment "Common options for DHCP applets" + depends on UDHCPD || UDHCPC || UDHCPC6 || DHCPRELAY + config FEATURE_UDHCP_PORT bool "Enable '-P port' option for udhcpd and udhcpc" default n - depends on UDHCPD || UDHCPC + depends on UDHCPD || UDHCPC || UDHCPC6 help At the cost of ~300 bytes, enables -P port option. This feature is typically not needed. config UDHCP_DEBUG - int "Maximum verbosity level for udhcp applets (0..9)" + int "Maximum verbosity level (0..9)" default 9 range 0 9 - depends on UDHCPD || UDHCPC || DHCPRELAY + depends on UDHCPD || UDHCPC || UDHCPC6 || DHCPRELAY help Verbosity can be increased with multiple -v options. This option controls how high it can be cranked up. @@ -122,23 +126,6 @@ config UDHCP_DEBUG Bigger values result in bigger code. Levels above 1 are very verbose and useful for debugging only. -config FEATURE_UDHCP_RFC3397 - bool "Support RFC3397 domain search (experimental)" - default y - depends on UDHCPD || UDHCPC - help - If selected, both client and server will support passing of domain - search lists via option 119, specified in RFC 3397, - and SIP servers option 120, specified in RFC 3361. - -config FEATURE_UDHCP_8021Q - bool "Support 802.1Q VLAN parameters" - default y - depends on UDHCPD || UDHCPC - help - If selected, both client and server will support passing of VLAN - ID and priority via options 132 and 133 as per 802.1Q. - config UDHCPC_SLACK_FOR_BUGGY_SERVERS int "DHCP options slack buffer size" default 80 @@ -149,10 +136,10 @@ config UDHCPC_SLACK_FOR_BUGGY_SERVERS field larger than we expect (which might also be considered a buffer overflow attempt). These packets are normally discarded. If circumstances beyond your control force you to support such - servers, this may help. The upper limit (924) makes dhcpc accept + servers, this may help. The upper limit (924) makes udhcpc accept even 1500 byte packets (maximum-sized ethernet packets). - This option does not make dhcp[cd] emit non-standard + This option does not make udhcp[cd] emit non-standard sized packets. Known buggy DHCP servers: @@ -161,3 +148,20 @@ config UDHCPC_SLACK_FOR_BUGGY_SERVERS maximum size of entire IP packet, and sends packets which are 28 bytes too large. Seednet (ISP) VDSL: sends packets 2 bytes too large. + +config FEATURE_UDHCP_RFC3397 + bool "Support RFC 3397 domain search options" + default y + depends on UDHCPD || UDHCPC + help + If selected, both client and server will support passing of domain + search lists via option 119, specified in RFC 3397, + and SIP servers option 120, specified in RFC 3361. + +config FEATURE_UDHCP_8021Q + bool "Support 802.1Q VLAN parameters options" + default y + depends on UDHCPD || UDHCPC + help + If selected, both client and server will support passing of VLAN + 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 @@ */ //config:config UDHCPC6 -//config: bool "udhcpc6 (DHCPv6 client, EXPERIMENTAL)" +//config: bool "udhcpc6" //config: default n # not yet ready //config: depends on FEATURE_IPV6 //config: help 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 @@ //config: select VOLUMEID //config: help //config: Lists labels and UUIDs of all filesystems. -//config: WARNING: -//config: With all submodules selected, it will add ~8k to busybox. //config: //config:config FEATURE_BLKID_TYPE //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 @@ //config: select VOLUMEID //config: help //config: Prints the name of a filesystem with given label or UUID. -//config: WARNING: -//config: With all submodules selected, it will add ~8k to busybox. /* Benefits from suid root: better access to /dev/BLOCKDEVs: */ //applet:IF_FINDFS(APPLET(findfs, BB_DIR_SBIN, BB_SUID_MAYBE)) 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 @@ //config: For more information, please see docs/mdev.txt //config: //config:config FEATURE_MDEV_LOAD_FIRMWARE -//config: bool "Support loading of firmwares" +//config: bool "Support loading of firmware" //config: default y //config: depends on MDEV //config: help diff --git a/util-linux/mount.c b/util-linux/mount.c index 1a39da2db..823b7c13b 100644 --- a/util-linux/mount.c +++ b/util-linux/mount.c @@ -26,8 +26,7 @@ //config: tree. The 'mount' utility is used to graft a filesystem onto a //config: particular part of the tree. A filesystem can either live on a block //config: device, or it can be accessible over the network, as is the case with -//config: NFS filesystems. Most people using BusyBox will also want to enable -//config: the 'mount' utility. +//config: NFS filesystems. //config: //config:config FEATURE_MOUNT_FAKE //config: bool "Support option -f" -- cgit v1.2.3-55-g6feb From 8cae43c5d732e86b8a668013b957fdb6363c8388 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 27 Jul 2017 10:58:08 +0200 Subject: swapon: do not use FEATURE_MOUNT_LABEL, have your own FEATURE_SWAPONOFF_LABEL Signed-off-by: Denys Vlasenko --- util-linux/mount.c | 1 - util-linux/swaponoff.c | 11 ++++++++++- util-linux/volume_id/get_devname.c | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/util-linux/mount.c b/util-linux/mount.c index 823b7c13b..5f030607c 100644 --- a/util-linux/mount.c +++ b/util-linux/mount.c @@ -64,7 +64,6 @@ //config: help //config: This allows for specifying a device by label or uuid, rather than by //config: name. This feature utilizes the same functionality as blkid/findfs. -//config: This also enables label or uuid support for swapon. //config: //config:config FEATURE_MOUNT_NFS //config: bool "Support mounting NFS file systems on Linux < 2.6.23" 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 @@ //config: bool "swapoff (4.3 kb)" //config: default y //config: select PLATFORM_LINUX +//config: +//config:config FEATURE_SWAPONOFF_LABEL +//config: bool "Support specifying devices by label or UUID" +//config: default y +//config: depends on SWAPON || SWAPOFF +//config: select VOLUMEID +//config: help +//config: This allows for specifying a device by label or uuid, rather than by +//config: name. This feature utilizes the same functionality as blkid/findfs. // APPLET_ODDNAME:name main location suid_type help //applet:IF_SWAPON( APPLET_ODDNAME(swapon, swap_on_off, BB_DIR_SBIN, BB_SUID_DROP, swapon)) @@ -72,7 +81,7 @@ # include #endif -#if ENABLE_FEATURE_MOUNT_LABEL +#if ENABLE_FEATURE_SWAPONOFF_LABEL # include "volume_id.h" #else # define resolve_mount_spec(fsname) ((void)0) 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 @@ //kbuild:lib-$(CONFIG_BLKID) += get_devname.o //kbuild:lib-$(CONFIG_FINDFS) += get_devname.o //kbuild:lib-$(CONFIG_FEATURE_MOUNT_LABEL) += get_devname.o +//kbuild:lib-$(CONFIG_FEATURE_SWAPONOFF_LABEL) += get_devname.o #include /* BLKGETSIZE64 */ #if !defined(BLKGETSIZE64) -- cgit v1.2.3-55-g6feb From 5ea50697fd13eb1caeecf3ceb8d03a1e7f578406 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 27 Jul 2017 11:17:15 +0200 Subject: ed: fix --help and reorder functions, no code changes Signed-off-by: Denys Vlasenko --- editors/ed.c | 1514 ++++++++++++++++++++++++++++------------------------------ 1 file changed, 741 insertions(+), 773 deletions(-) diff --git a/editors/ed.c b/editors/ed.c index c594d3da1..a2a389c2b 100644 --- a/editors/ed.c +++ b/editors/ed.c @@ -6,7 +6,6 @@ * * The "ed" built-in command (much simplified) */ - //config:config ED //config: bool "ed (25 kb)" //config: default y @@ -19,7 +18,7 @@ //applet:IF_ED(APPLET(ed, BB_DIR_BIN, BB_SUID_DROP)) -//usage:#define ed_trivial_usage "" +//usage:#define ed_trivial_usage "[FILE]" //usage:#define ed_full_usage "" #include "libbb.h" @@ -32,7 +31,6 @@ typedef struct LINE { char data[1]; } LINE; - #define searchString bb_common_bufsiz1 enum { @@ -71,22 +69,6 @@ struct globals { SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ } while (0) - -static void doCommands(void); -static void subCommand(const char *cmd, int num1, int num2); -static int getNum(const char **retcp, smallint *retHaveNum, int *retNum); -static int setCurNum(int num); -static void addLines(int num); -static int insertLine(int num, const char *data, int len); -static void deleteLines(int num1, int num2); -static int printLines(int num1, int num2, int expandFlag); -static int writeLines(const char *file, int num1, int num2); -static int readLines(const char *file, int num); -static int searchLines(const char *str, int num1, int num2); -static LINE *findLine(int num); -static int findString(const LINE *lp, const char * str, int len, int offset); - - static int bad_nums(int num1, int num2, const char *for_what) { if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { @@ -96,7 +78,6 @@ static int bad_nums(int num1, int num2, const char *for_what) return 0; } - static char *skip_blank(const char *cp) { while (isblank(*cp)) @@ -104,949 +85,936 @@ static char *skip_blank(const char *cp) return (char *)cp; } - -int ed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int ed_main(int argc UNUSED_PARAM, char **argv) +/* + * Return a pointer to the specified line number. + */ +static LINE *findLine(int num) { - INIT_G(); + LINE *lp; + int lnum; - bufSize = INITBUF_SIZE; - bufBase = xmalloc(bufSize); - bufPtr = bufBase; - lines.next = &lines; - lines.prev = &lines; + if ((num < 1) || (num > lastNum)) { + bb_error_msg("line number %d does not exist", num); + return NULL; + } - if (argv[1]) { - fileName = xstrdup(argv[1]); - if (!readLines(fileName, 1)) { - return EXIT_SUCCESS; - } - if (lastNum) - setCurNum(1); - dirty = FALSE; + if (curNum <= 0) { + curNum = 1; + curLine = lines.next; } - doCommands(); - return EXIT_SUCCESS; + if (num == curNum) + return curLine; + + lp = curLine; + lnum = curNum; + if (num < (curNum / 2)) { + lp = lines.next; + lnum = 1; + } else if (num > ((curNum + lastNum) / 2)) { + lp = lines.prev; + lnum = lastNum; + } + + while (lnum < num) { + lp = lp->next; + lnum++; + } + + while (lnum > num) { + lp = lp->prev; + lnum--; + } + return lp; } /* - * Read commands until we are told to stop. + * Search a line for the specified string starting at the specified + * offset in the line. Returns the offset of the found string, or -1. */ -static void doCommands(void) +static int findString(const LINE *lp, const char *str, int len, int offset) { - const char *cp; - char *endbuf, buf[USERSIZE]; - int len, num1, num2; - smallint have1, have2; + int left; + const char *cp, *ncp; - while (TRUE) { - /* Returns: - * -1 on read errors or EOF, or on bare Ctrl-D. - * 0 on ctrl-C, - * >0 length of input string, including terminating '\n' - */ - len = read_line_input(NULL, ": ", buf, sizeof(buf), /*timeout*/ -1); - if (len <= 0) - return; - endbuf = &buf[len - 1]; - while ((endbuf > buf) && isblank(endbuf[-1])) - endbuf--; - *endbuf = '\0'; + cp = &lp->data[offset]; + left = lp->len - offset; - cp = skip_blank(buf); - have1 = FALSE; - have2 = FALSE; + while (left >= len) { + ncp = memchr(cp, *str, left); + if (ncp == NULL) + return -1; + left -= (ncp - cp); + if (left < len) + return -1; + cp = ncp; + if (memcmp(cp, str, len) == 0) + return (cp - lp->data); + cp++; + left--; + } - if ((curNum == 0) && (lastNum > 0)) { - curNum = 1; - curLine = lines.next; - } + return -1; +} - if (!getNum(&cp, &have1, &num1)) - continue; +/* + * Search for a line which contains the specified string. + * If the string is "", then the previously searched for string + * is used. The currently searched for string is saved for future use. + * Returns the line number which matches, or 0 if there was no match + * with an error printed. + */ +static NOINLINE int searchLines(const char *str, int num1, int num2) +{ + const LINE *lp; + int len; - cp = skip_blank(cp); + if (bad_nums(num1, num2, "search")) + return 0; - if (*cp == ',') { - cp++; - if (!getNum(&cp, &have2, &num2)) - continue; - if (!have1) - num1 = 1; - if (!have2) - num2 = lastNum; - have1 = TRUE; - have2 = TRUE; + if (*str == '\0') { + if (searchString[0] == '\0') { + bb_error_msg("no previous search string"); + return 0; } - if (!have1) - num1 = curNum; - if (!have2) - num2 = num1; + str = searchString; + } - switch (*cp++) { - case 'a': - addLines(num1 + 1); - break; + if (str != searchString) + strcpy(searchString, str); - case 'c': - deleteLines(num1, num2); - addLines(num1); - break; + len = strlen(str); - case 'd': - deleteLines(num1, num2); - break; + lp = findLine(num1); + if (lp == NULL) + return 0; - case 'f': - if (*cp && !isblank(*cp)) { - bb_error_msg("bad file command"); - break; - } - cp = skip_blank(cp); - if (*cp == '\0') { - if (fileName) - printf("\"%s\"\n", fileName); - else - puts("No file name"); - break; - } - free(fileName); - fileName = xstrdup(cp); - break; + while (num1 <= num2) { + if (findString(lp, str, len, 0) >= 0) + return num1; + num1++; + lp = lp->next; + } - case 'i': - addLines(num1); - break; + bb_error_msg("can't find string \"%s\"", str); + return 0; +} - case 'k': - cp = skip_blank(cp); - if ((*cp < 'a') || (*cp > 'z') || cp[1]) { - bb_error_msg("bad mark name"); - break; - } - marks[*cp - 'a'] = num2; - break; +/* + * Parse a line number argument if it is present. This is a sum + * or difference of numbers, '.', '$', 'x, or a search string. + * Returns TRUE if successful (whether or not there was a number). + * Returns FALSE if there was a parsing error, with a message output. + * Whether there was a number is returned indirectly, as is the number. + * The character pointer which stopped the scan is also returned. + */ +static int getNum(const char **retcp, smallint *retHaveNum, int *retNum) +{ + const char *cp; + char *endStr, str[USERSIZE]; + int value, num; + smallint haveNum, minus; - case 'l': - printLines(num1, num2, TRUE); - break; + cp = *retcp; + value = 0; + haveNum = FALSE; + minus = 0; - case 'p': - printLines(num1, num2, FALSE); - break; + while (TRUE) { + cp = skip_blank(cp); - case 'q': - cp = skip_blank(cp); - if (have1 || *cp) { - bb_error_msg("bad quit command"); + switch (*cp) { + case '.': + haveNum = TRUE; + num = curNum; + cp++; break; - } - if (!dirty) - return; - len = read_line_input(NULL, "Really quit? ", buf, 16, /*timeout*/ -1); - /* read error/EOF - no way to continue */ - if (len < 0) - return; - cp = skip_blank(buf); - if ((*cp | 0x20) == 'y') /* Y or y */ - return; - break; - case 'r': - if (*cp && !isblank(*cp)) { - bb_error_msg("bad read command"); - break; - } - cp = skip_blank(cp); - if (*cp == '\0') { - bb_error_msg("no file name"); + case '$': + haveNum = TRUE; + num = lastNum; + cp++; break; - } - if (!have1) - num1 = lastNum; - if (readLines(cp, num1 + 1)) - break; - if (fileName == NULL) - fileName = xstrdup(cp); - break; - case 's': - subCommand(cp, num1, num2); - break; - - case 'w': - if (*cp && !isblank(*cp)) { - bb_error_msg("bad write command"); - break; - } - cp = skip_blank(cp); - if (!have1) { - num1 = 1; - num2 = lastNum; - } - if (*cp == '\0') - cp = fileName; - if (cp == NULL) { - bb_error_msg("no file name specified"); + case '\'': + cp++; + if ((*cp < 'a') || (*cp > 'z')) { + bb_error_msg("bad mark name"); + return FALSE; + } + haveNum = TRUE; + num = marks[*cp++ - 'a']; break; - } - writeLines(cp, num1, num2); - break; - case 'z': - switch (*cp) { - case '-': - printLines(curNum - 21, curNum, FALSE); - break; - case '.': - printLines(curNum - 11, curNum + 10, FALSE); + case '/': + strcpy(str, ++cp); + endStr = strchr(str, '/'); + if (endStr) { + *endStr++ = '\0'; + cp += (endStr - str); + } else + cp = ""; + num = searchLines(str, curNum, lastNum); + if (num == 0) + return FALSE; + haveNum = TRUE; break; + default: - printLines(curNum, curNum + 21, FALSE); + if (!isdigit(*cp)) { + *retcp = cp; + *retHaveNum = haveNum; + *retNum = value; + return TRUE; + } + num = 0; + while (isdigit(*cp)) + num = num * 10 + *cp++ - '0'; + haveNum = TRUE; break; - } - break; + } - case '.': - if (have1) { - bb_error_msg("no arguments allowed"); - break; - } - printLines(curNum, curNum, FALSE); - break; + value += (minus ? -num : num); - case '-': - if (setCurNum(curNum - 1)) - printLines(curNum, curNum, FALSE); - break; + cp = skip_blank(cp); - case '=': - printf("%d\n", num1); - break; - case '\0': - if (have1) { - printLines(num2, num2, FALSE); + switch (*cp) { + case '-': + minus = 1; + cp++; break; - } - if (setCurNum(curNum + 1)) - printLines(curNum, curNum, FALSE); - break; - default: - bb_error_msg("unimplemented command"); - break; + case '+': + minus = 0; + cp++; + break; + + default: + *retcp = cp; + *retHaveNum = haveNum; + *retNum = value; + return TRUE; } } } - /* - * Do the substitute command. - * The current line is set to the last substitution done. + * Set the current line number. + * Returns TRUE if successful. */ -static void subCommand(const char *cmd, int num1, int num2) +static int setCurNum(int num) { - char *cp, *oldStr, *newStr, buf[USERSIZE]; - int delim, oldLen, newLen, deltaLen, offset; - LINE *lp, *nlp; - int globalFlag, printFlag, didSub, needPrint; - - if (bad_nums(num1, num2, "substitute")) - return; + LINE *lp; - globalFlag = FALSE; - printFlag = FALSE; - didSub = FALSE; - needPrint = FALSE; + lp = findLine(num); + if (lp == NULL) + return FALSE; + curNum = num; + curLine = lp; + return TRUE; +} - /* - * Copy the command so we can modify it. - */ - strcpy(buf, cmd); - cp = buf; +/* + * Insert a new line with the specified text. + * The line is inserted so as to become the specified line, + * thus pushing any existing and further lines down one. + * The inserted line is also set to become the current line. + * Returns TRUE if successful. + */ +static int insertLine(int num, const char *data, int len) +{ + LINE *newLp, *lp; - if (isblank(*cp) || (*cp == '\0')) { - bb_error_msg("bad delimiter for substitute"); - return; + if ((num < 1) || (num > lastNum + 1)) { + bb_error_msg("inserting at bad line number"); + return FALSE; } - delim = *cp++; - oldStr = cp; + newLp = xmalloc(sizeof(LINE) + len - 1); - cp = strchr(cp, delim); - if (cp == NULL) { - bb_error_msg("missing 2nd delimiter for substitute"); - return; - } + memcpy(newLp->data, data, len); + newLp->len = len; - *cp++ = '\0'; + if (num > lastNum) + lp = &lines; + else { + lp = findLine(num); + if (lp == NULL) { + free((char *) newLp); + return FALSE; + } + } - newStr = cp; - cp = strchr(cp, delim); + newLp->next = lp; + newLp->prev = lp->prev; + lp->prev->next = newLp; + lp->prev = newLp; - if (cp) - *cp++ = '\0'; - else - cp = (char*)""; + lastNum++; + dirty = TRUE; + return setCurNum(num); +} - while (*cp) switch (*cp++) { - case 'g': - globalFlag = TRUE; - break; - case 'p': - printFlag = TRUE; - break; - default: - bb_error_msg("unknown option for substitute"); - return; - } +/* + * Add lines which are typed in by the user. + * The lines are inserted just before the specified line number. + * The lines are terminated by a line containing a single dot (ugly!), + * or by an end of file. + */ +static void addLines(int num) +{ + int len; + char buf[USERSIZE + 1]; - if (*oldStr == '\0') { - if (searchString[0] == '\0') { - bb_error_msg("no previous search string"); + while (1) { + /* Returns: + * -1 on read errors or EOF, or on bare Ctrl-D. + * 0 on ctrl-C, + * >0 length of input string, including terminating '\n' + */ + len = read_line_input(NULL, "", buf, sizeof(buf), /*timeout*/ -1); + if (len <= 0) { + /* Previously, ctrl-C was exiting to shell. + * Now we exit to ed prompt. Is in important? */ return; } - oldStr = searchString; + if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0')) + return; + if (!insertLine(num++, buf, len)) + return; } +} - if (oldStr != searchString) - strcpy(searchString, oldStr); - - lp = findLine(num1); - if (lp == NULL) - return; +/* + * Read lines from a file at the specified line number. + * Returns TRUE if the file was successfully read. + */ +static int readLines(const char *file, int num) +{ + int fd, cc; + int len, lineCount, charCount; + char *cp; - oldLen = strlen(oldStr); - newLen = strlen(newStr); - deltaLen = newLen - oldLen; - offset = 0; - nlp = NULL; + if ((num < 1) || (num > lastNum + 1)) { + bb_error_msg("bad line for read"); + return FALSE; + } - while (num1 <= num2) { - offset = findString(lp, oldStr, oldLen, offset); + fd = open(file, 0); + if (fd < 0) { + bb_simple_perror_msg(file); + return FALSE; + } - if (offset < 0) { - if (needPrint) { - printLines(num1, num1, FALSE); - needPrint = FALSE; - } - offset = 0; - lp = lp->next; - num1++; - continue; - } + bufPtr = bufBase; + bufUsed = 0; + lineCount = 0; + charCount = 0; + cc = 0; - needPrint = printFlag; - didSub = TRUE; - dirty = TRUE; + printf("\"%s\", ", file); + fflush_all(); - /* - * If the replacement string is the same size or shorter - * than the old string, then the substitution is easy. - */ - if (deltaLen <= 0) { - memcpy(&lp->data[offset], newStr, newLen); - if (deltaLen) { - memcpy(&lp->data[offset + newLen], - &lp->data[offset + oldLen], - lp->len - offset - oldLen); + do { + cp = memchr(bufPtr, '\n', bufUsed); - lp->len += deltaLen; - } - offset += newLen; - if (globalFlag) - continue; - if (needPrint) { - printLines(num1, num1, FALSE); - needPrint = FALSE; + if (cp) { + len = (cp - bufPtr) + 1; + if (!insertLine(num, bufPtr, len)) { + close(fd); + return FALSE; } - lp = lp->next; - num1++; + bufPtr += len; + bufUsed -= len; + charCount += len; + lineCount++; + num++; continue; } - /* - * The new string is larger, so allocate a new line - * structure and use that. Link it in place of - * the old line structure. - */ - nlp = xmalloc(sizeof(LINE) + lp->len + deltaLen); - - nlp->len = lp->len + deltaLen; - - memcpy(nlp->data, lp->data, offset); - memcpy(&nlp->data[offset], newStr, newLen); - memcpy(&nlp->data[offset + newLen], - &lp->data[offset + oldLen], - lp->len - offset - oldLen); - - nlp->next = lp->next; - nlp->prev = lp->prev; - nlp->prev->next = nlp; - nlp->next->prev = nlp; - - if (curLine == lp) - curLine = nlp; + if (bufPtr != bufBase) { + memcpy(bufBase, bufPtr, bufUsed); + bufPtr = bufBase + bufUsed; + } - free(lp); - lp = nlp; + if (bufUsed >= bufSize) { + len = (bufSize * 3) / 2; + cp = xrealloc(bufBase, len); + bufBase = cp; + bufPtr = bufBase + bufUsed; + bufSize = len; + } - offset += newLen; + cc = safe_read(fd, bufPtr, bufSize - bufUsed); + bufUsed += cc; + bufPtr = bufBase; + } while (cc > 0); - if (globalFlag) - continue; + if (cc < 0) { + bb_simple_perror_msg(file); + close(fd); + return FALSE; + } - if (needPrint) { - printLines(num1, num1, FALSE); - needPrint = FALSE; + if (bufUsed) { + if (!insertLine(num, bufPtr, bufUsed)) { + close(fd); + return -1; } - - lp = lp->next; - num1++; + lineCount++; + charCount += bufUsed; } - if (!didSub) - bb_error_msg("no substitutions found for \"%s\"", oldStr); -} + close(fd); + + printf("%d lines%s, %d chars\n", lineCount, + (bufUsed ? " (incomplete)" : ""), charCount); + return TRUE; +} /* - * Search a line for the specified string starting at the specified - * offset in the line. Returns the offset of the found string, or -1. + * Write the specified lines out to the specified file. + * Returns TRUE if successful, or FALSE on an error with a message output. */ -static int findString(const LINE *lp, const char *str, int len, int offset) +static int writeLines(const char *file, int num1, int num2) { - int left; - const char *cp, *ncp; + LINE *lp; + int fd, lineCount, charCount; - cp = &lp->data[offset]; - left = lp->len - offset; + if (bad_nums(num1, num2, "write")) + return FALSE; - while (left >= len) { - ncp = memchr(cp, *str, left); - if (ncp == NULL) - return -1; - left -= (ncp - cp); - if (left < len) - return -1; - cp = ncp; - if (memcmp(cp, str, len) == 0) - return (cp - lp->data); - cp++; - left--; - } + lineCount = 0; + charCount = 0; - return -1; -} + fd = creat(file, 0666); + if (fd < 0) { + bb_simple_perror_msg(file); + return FALSE; + } + printf("\"%s\", ", file); + fflush_all(); -/* - * Add lines which are typed in by the user. - * The lines are inserted just before the specified line number. - * The lines are terminated by a line containing a single dot (ugly!), - * or by an end of file. - */ -static void addLines(int num) -{ - int len; - char buf[USERSIZE + 1]; + lp = findLine(num1); + if (lp == NULL) { + close(fd); + return FALSE; + } - while (1) { - /* Returns: - * -1 on read errors or EOF, or on bare Ctrl-D. - * 0 on ctrl-C, - * >0 length of input string, including terminating '\n' - */ - len = read_line_input(NULL, "", buf, sizeof(buf), /*timeout*/ -1); - if (len <= 0) { - /* Previously, ctrl-C was exiting to shell. - * Now we exit to ed prompt. Is in important? */ - return; + while (num1++ <= num2) { + if (full_write(fd, lp->data, lp->len) != lp->len) { + bb_simple_perror_msg(file); + close(fd); + return FALSE; } - if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0')) - return; - if (!insertLine(num++, buf, len)) - return; + charCount += lp->len; + lineCount++; + lp = lp->next; + } + + if (close(fd) < 0) { + bb_simple_perror_msg(file); + return FALSE; } -} + printf("%d lines, %d chars\n", lineCount, charCount); + return TRUE; +} /* - * Parse a line number argument if it is present. This is a sum - * or difference of numbers, '.', '$', 'x, or a search string. - * Returns TRUE if successful (whether or not there was a number). - * Returns FALSE if there was a parsing error, with a message output. - * Whether there was a number is returned indirectly, as is the number. - * The character pointer which stopped the scan is also returned. + * Print lines in a specified range. + * The last line printed becomes the current line. + * If expandFlag is TRUE, then the line is printed specially to + * show magic characters. */ -static int getNum(const char **retcp, smallint *retHaveNum, int *retNum) +static int printLines(int num1, int num2, int expandFlag) { + const LINE *lp; const char *cp; - char *endStr, str[USERSIZE]; - int value, num; - smallint haveNum, minus; + int ch, count; - cp = *retcp; - value = 0; - haveNum = FALSE; - minus = 0; + if (bad_nums(num1, num2, "print")) + return FALSE; - while (TRUE) { - cp = skip_blank(cp); + lp = findLine(num1); + if (lp == NULL) + return FALSE; - switch (*cp) { - case '.': - haveNum = TRUE; - num = curNum; - cp++; - break; + while (num1 <= num2) { + if (!expandFlag) { + write(STDOUT_FILENO, lp->data, lp->len); + setCurNum(num1++); + lp = lp->next; + continue; + } - case '$': - haveNum = TRUE; - num = lastNum; - cp++; - break; - - case '\'': - cp++; - if ((*cp < 'a') || (*cp > 'z')) { - bb_error_msg("bad mark name"); - return FALSE; - } - haveNum = TRUE; - num = marks[*cp++ - 'a']; - break; + /* + * Show control characters and characters with the + * high bit set specially. + */ + cp = lp->data; + count = lp->len; - case '/': - strcpy(str, ++cp); - endStr = strchr(str, '/'); - if (endStr) { - *endStr++ = '\0'; - cp += (endStr - str); - } else - cp = ""; - num = searchLines(str, curNum, lastNum); - if (num == 0) - return FALSE; - haveNum = TRUE; - break; + if ((count > 0) && (cp[count - 1] == '\n')) + count--; - default: - if (!isdigit(*cp)) { - *retcp = cp; - *retHaveNum = haveNum; - *retNum = value; - return TRUE; - } - num = 0; - while (isdigit(*cp)) - num = num * 10 + *cp++ - '0'; - haveNum = TRUE; - break; + while (count-- > 0) { + ch = (unsigned char) *cp++; + fputc_printable(ch | PRINTABLE_META, stdout); } - value += (minus ? -num : num); - - cp = skip_blank(cp); - - switch (*cp) { - case '-': - minus = 1; - cp++; - break; - - case '+': - minus = 0; - cp++; - break; + fputs("$\n", stdout); - default: - *retcp = cp; - *retHaveNum = haveNum; - *retNum = value; - return TRUE; - } + setCurNum(num1++); + lp = lp->next; } -} + return TRUE; +} /* - * Read lines from a file at the specified line number. - * Returns TRUE if the file was successfully read. + * Delete lines from the given range. */ -static int readLines(const char *file, int num) +static void deleteLines(int num1, int num2) { - int fd, cc; - int len, lineCount, charCount; - char *cp; + LINE *lp, *nlp, *plp; + int count; - if ((num < 1) || (num > lastNum + 1)) { - bb_error_msg("bad line for read"); - return FALSE; - } + if (bad_nums(num1, num2, "delete")) + return; - fd = open(file, 0); - if (fd < 0) { - bb_simple_perror_msg(file); - return FALSE; - } + lp = findLine(num1); + if (lp == NULL) + return; - bufPtr = bufBase; - bufUsed = 0; - lineCount = 0; - charCount = 0; - cc = 0; + if ((curNum >= num1) && (curNum <= num2)) { + if (num2 < lastNum) + setCurNum(num2 + 1); + else if (num1 > 1) + setCurNum(num1 - 1); + else + curNum = 0; + } - printf("\"%s\", ", file); - fflush_all(); + count = num2 - num1 + 1; + if (curNum > num2) + curNum -= count; + lastNum -= count; - do { - cp = memchr(bufPtr, '\n', bufUsed); + while (count-- > 0) { + nlp = lp->next; + plp = lp->prev; + plp->next = nlp; + nlp->prev = plp; + free(lp); + lp = nlp; + } - if (cp) { - len = (cp - bufPtr) + 1; - if (!insertLine(num, bufPtr, len)) { - close(fd); - return FALSE; - } - bufPtr += len; - bufUsed -= len; - charCount += len; - lineCount++; - num++; - continue; - } + dirty = TRUE; +} - if (bufPtr != bufBase) { - memcpy(bufBase, bufPtr, bufUsed); - bufPtr = bufBase + bufUsed; - } +/* + * Do the substitute command. + * The current line is set to the last substitution done. + */ +static void subCommand(const char *cmd, int num1, int num2) +{ + char *cp, *oldStr, *newStr, buf[USERSIZE]; + int delim, oldLen, newLen, deltaLen, offset; + LINE *lp, *nlp; + int globalFlag, printFlag, didSub, needPrint; - if (bufUsed >= bufSize) { - len = (bufSize * 3) / 2; - cp = xrealloc(bufBase, len); - bufBase = cp; - bufPtr = bufBase + bufUsed; - bufSize = len; - } + if (bad_nums(num1, num2, "substitute")) + return; - cc = safe_read(fd, bufPtr, bufSize - bufUsed); - bufUsed += cc; - bufPtr = bufBase; - } while (cc > 0); + globalFlag = FALSE; + printFlag = FALSE; + didSub = FALSE; + needPrint = FALSE; - if (cc < 0) { - bb_simple_perror_msg(file); - close(fd); - return FALSE; - } + /* + * Copy the command so we can modify it. + */ + strcpy(buf, cmd); + cp = buf; - if (bufUsed) { - if (!insertLine(num, bufPtr, bufUsed)) { - close(fd); - return -1; - } - lineCount++; - charCount += bufUsed; + if (isblank(*cp) || (*cp == '\0')) { + bb_error_msg("bad delimiter for substitute"); + return; } - close(fd); - - printf("%d lines%s, %d chars\n", lineCount, - (bufUsed ? " (incomplete)" : ""), charCount); + delim = *cp++; + oldStr = cp; - return TRUE; -} + cp = strchr(cp, delim); + if (cp == NULL) { + bb_error_msg("missing 2nd delimiter for substitute"); + return; + } + *cp++ = '\0'; -/* - * Write the specified lines out to the specified file. - * Returns TRUE if successful, or FALSE on an error with a message output. - */ -static int writeLines(const char *file, int num1, int num2) -{ - LINE *lp; - int fd, lineCount, charCount; + newStr = cp; + cp = strchr(cp, delim); - if (bad_nums(num1, num2, "write")) - return FALSE; + if (cp) + *cp++ = '\0'; + else + cp = (char*)""; - lineCount = 0; - charCount = 0; + while (*cp) switch (*cp++) { + case 'g': + globalFlag = TRUE; + break; + case 'p': + printFlag = TRUE; + break; + default: + bb_error_msg("unknown option for substitute"); + return; + } - fd = creat(file, 0666); - if (fd < 0) { - bb_simple_perror_msg(file); - return FALSE; + if (*oldStr == '\0') { + if (searchString[0] == '\0') { + bb_error_msg("no previous search string"); + return; + } + oldStr = searchString; } - printf("\"%s\", ", file); - fflush_all(); + if (oldStr != searchString) + strcpy(searchString, oldStr); lp = findLine(num1); - if (lp == NULL) { - close(fd); - return FALSE; - } + if (lp == NULL) + return; - while (num1++ <= num2) { - if (full_write(fd, lp->data, lp->len) != lp->len) { - bb_simple_perror_msg(file); - close(fd); - return FALSE; - } - charCount += lp->len; - lineCount++; - lp = lp->next; - } - - if (close(fd) < 0) { - bb_simple_perror_msg(file); - return FALSE; - } - - printf("%d lines, %d chars\n", lineCount, charCount); - return TRUE; -} - - -/* - * Print lines in a specified range. - * The last line printed becomes the current line. - * If expandFlag is TRUE, then the line is printed specially to - * show magic characters. - */ -static int printLines(int num1, int num2, int expandFlag) -{ - const LINE *lp; - const char *cp; - int ch, count; - - if (bad_nums(num1, num2, "print")) - return FALSE; - - lp = findLine(num1); - if (lp == NULL) - return FALSE; + oldLen = strlen(oldStr); + newLen = strlen(newStr); + deltaLen = newLen - oldLen; + offset = 0; + nlp = NULL; while (num1 <= num2) { - if (!expandFlag) { - write(STDOUT_FILENO, lp->data, lp->len); - setCurNum(num1++); + offset = findString(lp, oldStr, oldLen, offset); + + if (offset < 0) { + if (needPrint) { + printLines(num1, num1, FALSE); + needPrint = FALSE; + } + offset = 0; lp = lp->next; + num1++; continue; } + needPrint = printFlag; + didSub = TRUE; + dirty = TRUE; + /* - * Show control characters and characters with the - * high bit set specially. + * If the replacement string is the same size or shorter + * than the old string, then the substitution is easy. */ - cp = lp->data; - count = lp->len; - - if ((count > 0) && (cp[count - 1] == '\n')) - count--; + if (deltaLen <= 0) { + memcpy(&lp->data[offset], newStr, newLen); + if (deltaLen) { + memcpy(&lp->data[offset + newLen], + &lp->data[offset + oldLen], + lp->len - offset - oldLen); - while (count-- > 0) { - ch = (unsigned char) *cp++; - fputc_printable(ch | PRINTABLE_META, stdout); + lp->len += deltaLen; + } + offset += newLen; + if (globalFlag) + continue; + if (needPrint) { + printLines(num1, num1, FALSE); + needPrint = FALSE; + } + lp = lp->next; + num1++; + continue; } - fputs("$\n", stdout); + /* + * The new string is larger, so allocate a new line + * structure and use that. Link it in place of + * the old line structure. + */ + nlp = xmalloc(sizeof(LINE) + lp->len + deltaLen); - setCurNum(num1++); - lp = lp->next; - } + nlp->len = lp->len + deltaLen; - return TRUE; -} + memcpy(nlp->data, lp->data, offset); + memcpy(&nlp->data[offset], newStr, newLen); + memcpy(&nlp->data[offset + newLen], + &lp->data[offset + oldLen], + lp->len - offset - oldLen); + nlp->next = lp->next; + nlp->prev = lp->prev; + nlp->prev->next = nlp; + nlp->next->prev = nlp; -/* - * Insert a new line with the specified text. - * The line is inserted so as to become the specified line, - * thus pushing any existing and further lines down one. - * The inserted line is also set to become the current line. - * Returns TRUE if successful. - */ -static int insertLine(int num, const char *data, int len) -{ - LINE *newLp, *lp; + if (curLine == lp) + curLine = nlp; - if ((num < 1) || (num > lastNum + 1)) { - bb_error_msg("inserting at bad line number"); - return FALSE; - } + free(lp); + lp = nlp; - newLp = xmalloc(sizeof(LINE) + len - 1); + offset += newLen; - memcpy(newLp->data, data, len); - newLp->len = len; + if (globalFlag) + continue; - if (num > lastNum) - lp = &lines; - else { - lp = findLine(num); - if (lp == NULL) { - free((char *) newLp); - return FALSE; + if (needPrint) { + printLines(num1, num1, FALSE); + needPrint = FALSE; } - } - newLp->next = lp; - newLp->prev = lp->prev; - lp->prev->next = newLp; - lp->prev = newLp; + lp = lp->next; + num1++; + } - lastNum++; - dirty = TRUE; - return setCurNum(num); + if (!didSub) + bb_error_msg("no substitutions found for \"%s\"", oldStr); } - /* - * Delete lines from the given range. + * Read commands until we are told to stop. */ -static void deleteLines(int num1, int num2) +static void doCommands(void) { - LINE *lp, *nlp, *plp; - int count; + const char *cp; + char *endbuf, buf[USERSIZE]; + int len, num1, num2; + smallint have1, have2; - if (bad_nums(num1, num2, "delete")) - return; + while (TRUE) { + /* Returns: + * -1 on read errors or EOF, or on bare Ctrl-D. + * 0 on ctrl-C, + * >0 length of input string, including terminating '\n' + */ + len = read_line_input(NULL, ": ", buf, sizeof(buf), /*timeout*/ -1); + if (len <= 0) + return; + endbuf = &buf[len - 1]; + while ((endbuf > buf) && isblank(endbuf[-1])) + endbuf--; + *endbuf = '\0'; - lp = findLine(num1); - if (lp == NULL) - return; + cp = skip_blank(buf); + have1 = FALSE; + have2 = FALSE; - if ((curNum >= num1) && (curNum <= num2)) { - if (num2 < lastNum) - setCurNum(num2 + 1); - else if (num1 > 1) - setCurNum(num1 - 1); - else - curNum = 0; - } + if ((curNum == 0) && (lastNum > 0)) { + curNum = 1; + curLine = lines.next; + } - count = num2 - num1 + 1; - if (curNum > num2) - curNum -= count; - lastNum -= count; + if (!getNum(&cp, &have1, &num1)) + continue; - while (count-- > 0) { - nlp = lp->next; - plp = lp->prev; - plp->next = nlp; - nlp->prev = plp; - free(lp); - lp = nlp; - } + cp = skip_blank(cp); - dirty = TRUE; -} + if (*cp == ',') { + cp++; + if (!getNum(&cp, &have2, &num2)) + continue; + if (!have1) + num1 = 1; + if (!have2) + num2 = lastNum; + have1 = TRUE; + have2 = TRUE; + } + if (!have1) + num1 = curNum; + if (!have2) + num2 = num1; + switch (*cp++) { + case 'a': + addLines(num1 + 1); + break; -/* - * Search for a line which contains the specified string. - * If the string is "", then the previously searched for string - * is used. The currently searched for string is saved for future use. - * Returns the line number which matches, or 0 if there was no match - * with an error printed. - */ -static NOINLINE int searchLines(const char *str, int num1, int num2) -{ - const LINE *lp; - int len; + case 'c': + deleteLines(num1, num2); + addLines(num1); + break; - if (bad_nums(num1, num2, "search")) - return 0; + case 'd': + deleteLines(num1, num2); + break; - if (*str == '\0') { - if (searchString[0] == '\0') { - bb_error_msg("no previous search string"); - return 0; - } - str = searchString; - } + case 'f': + if (*cp && !isblank(*cp)) { + bb_error_msg("bad file command"); + break; + } + cp = skip_blank(cp); + if (*cp == '\0') { + if (fileName) + printf("\"%s\"\n", fileName); + else + puts("No file name"); + break; + } + free(fileName); + fileName = xstrdup(cp); + break; - if (str != searchString) - strcpy(searchString, str); + case 'i': + addLines(num1); + break; - len = strlen(str); + case 'k': + cp = skip_blank(cp); + if ((*cp < 'a') || (*cp > 'z') || cp[1]) { + bb_error_msg("bad mark name"); + break; + } + marks[*cp - 'a'] = num2; + break; - lp = findLine(num1); - if (lp == NULL) - return 0; + case 'l': + printLines(num1, num2, TRUE); + break; - while (num1 <= num2) { - if (findString(lp, str, len, 0) >= 0) - return num1; - num1++; - lp = lp->next; - } + case 'p': + printLines(num1, num2, FALSE); + break; - bb_error_msg("can't find string \"%s\"", str); - return 0; -} + case 'q': + cp = skip_blank(cp); + if (have1 || *cp) { + bb_error_msg("bad quit command"); + break; + } + if (!dirty) + return; + len = read_line_input(NULL, "Really quit? ", buf, 16, /*timeout*/ -1); + /* read error/EOF - no way to continue */ + if (len < 0) + return; + cp = skip_blank(buf); + if ((*cp | 0x20) == 'y') /* Y or y */ + return; + break; + case 'r': + if (*cp && !isblank(*cp)) { + bb_error_msg("bad read command"); + break; + } + cp = skip_blank(cp); + if (*cp == '\0') { + bb_error_msg("no file name"); + break; + } + if (!have1) + num1 = lastNum; + if (readLines(cp, num1 + 1)) + break; + if (fileName == NULL) + fileName = xstrdup(cp); + break; -/* - * Return a pointer to the specified line number. - */ -static LINE *findLine(int num) -{ - LINE *lp; - int lnum; + case 's': + subCommand(cp, num1, num2); + break; - if ((num < 1) || (num > lastNum)) { - bb_error_msg("line number %d does not exist", num); - return NULL; - } + case 'w': + if (*cp && !isblank(*cp)) { + bb_error_msg("bad write command"); + break; + } + cp = skip_blank(cp); + if (!have1) { + num1 = 1; + num2 = lastNum; + } + if (*cp == '\0') + cp = fileName; + if (cp == NULL) { + bb_error_msg("no file name specified"); + break; + } + writeLines(cp, num1, num2); + break; - if (curNum <= 0) { - curNum = 1; - curLine = lines.next; - } + case 'z': + switch (*cp) { + case '-': + printLines(curNum - 21, curNum, FALSE); + break; + case '.': + printLines(curNum - 11, curNum + 10, FALSE); + break; + default: + printLines(curNum, curNum + 21, FALSE); + break; + } + break; - if (num == curNum) - return curLine; + case '.': + if (have1) { + bb_error_msg("no arguments allowed"); + break; + } + printLines(curNum, curNum, FALSE); + break; - lp = curLine; - lnum = curNum; - if (num < (curNum / 2)) { - lp = lines.next; - lnum = 1; - } else if (num > ((curNum + lastNum) / 2)) { - lp = lines.prev; - lnum = lastNum; - } + case '-': + if (setCurNum(curNum - 1)) + printLines(curNum, curNum, FALSE); + break; - while (lnum < num) { - lp = lp->next; - lnum++; - } + case '=': + printf("%d\n", num1); + break; + case '\0': + if (have1) { + printLines(num2, num2, FALSE); + break; + } + if (setCurNum(curNum + 1)) + printLines(curNum, curNum, FALSE); + break; - while (lnum > num) { - lp = lp->prev; - lnum--; + default: + bb_error_msg("unimplemented command"); + break; + } } - return lp; } - -/* - * Set the current line number. - * Returns TRUE if successful. - */ -static int setCurNum(int num) +int ed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int ed_main(int argc UNUSED_PARAM, char **argv) { - LINE *lp; + INIT_G(); - lp = findLine(num); - if (lp == NULL) - return FALSE; - curNum = num; - curLine = lp; - return TRUE; + bufSize = INITBUF_SIZE; + bufBase = xmalloc(bufSize); + bufPtr = bufBase; + lines.next = &lines; + lines.prev = &lines; + + if (argv[1]) { + fileName = xstrdup(argv[1]); + if (!readLines(fileName, 1)) { + return EXIT_SUCCESS; + } + if (lastNum) + setCurNum(1); + dirty = FALSE; + } + + doCommands(); + return EXIT_SUCCESS; } -- cgit v1.2.3-55-g6feb From 7d2f33dc1f6dcd44671d88360bc598ad82c37a60 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 27 Jul 2017 11:58:25 +0200 Subject: ed: code shrink function old new delta findString 117 115 -2 skip_blank 16 - -16 getNum 369 345 -24 doCommands 2448 2183 -265 ------------------------------------------------------------------------------ (add/remove: 0/1 grow/shrink: 0/3 up/down: 0/-307) Total: -307 bytes Signed-off-by: Denys Vlasenko --- editors/ed.c | 94 +++++++++++++++++++++++++++--------------------------------- 1 file changed, 43 insertions(+), 51 deletions(-) diff --git a/editors/ed.c b/editors/ed.c index a2a389c2b..b13c3ed59 100644 --- a/editors/ed.c +++ b/editors/ed.c @@ -78,13 +78,6 @@ static int bad_nums(int num1, int num2, const char *for_what) return 0; } -static char *skip_blank(const char *cp) -{ - while (isblank(*cp)) - cp++; - return (char *)cp; -} - /* * Return a pointer to the specified line number. */ @@ -138,15 +131,13 @@ static int findString(const LINE *lp, const char *str, int len, int offset) const char *cp, *ncp; cp = &lp->data[offset]; - left = lp->len - offset; + left = lp->len - offset - len; - while (left >= len) { - ncp = memchr(cp, *str, left); + while (left >= 0) { + ncp = memchr(cp, str[0], left + 1); if (ncp == NULL) return -1; left -= (ncp - cp); - if (left < len) - return -1; cp = ncp; if (memcmp(cp, str, len) == 0) return (cp - lp->data); @@ -203,25 +194,23 @@ static NOINLINE int searchLines(const char *str, int num1, int num2) /* * Parse a line number argument if it is present. This is a sum * or difference of numbers, '.', '$', 'x, or a search string. - * Returns TRUE if successful (whether or not there was a number). - * Returns FALSE if there was a parsing error, with a message output. + * Returns pointer which stopped the scan if successful (whether or not + * there was a number). + * Returns NULL if there was a parsing error, with a message output. * Whether there was a number is returned indirectly, as is the number. - * The character pointer which stopped the scan is also returned. */ -static int getNum(const char **retcp, smallint *retHaveNum, int *retNum) +static const char* getNum(const char *cp, smallint *retHaveNum, int *retNum) { - const char *cp; char *endStr, str[USERSIZE]; int value, num; smallint haveNum, minus; - cp = *retcp; value = 0; haveNum = FALSE; minus = 0; while (TRUE) { - cp = skip_blank(cp); + cp = skip_whitespace(cp); switch (*cp) { case '.': @@ -240,7 +229,7 @@ static int getNum(const char **retcp, smallint *retHaveNum, int *retNum) cp++; if ((*cp < 'a') || (*cp > 'z')) { bb_error_msg("bad mark name"); - return FALSE; + return NULL; } haveNum = TRUE; num = marks[*cp++ - 'a']; @@ -256,16 +245,15 @@ static int getNum(const char **retcp, smallint *retHaveNum, int *retNum) cp = ""; num = searchLines(str, curNum, lastNum); if (num == 0) - return FALSE; + return NULL; haveNum = TRUE; break; default: if (!isdigit(*cp)) { - *retcp = cp; *retHaveNum = haveNum; *retNum = value; - return TRUE; + return cp; } num = 0; while (isdigit(*cp)) @@ -276,7 +264,7 @@ static int getNum(const char **retcp, smallint *retHaveNum, int *retNum) value += (minus ? -num : num); - cp = skip_blank(cp); + cp = skip_whitespace(cp); switch (*cp) { case '-': @@ -290,10 +278,9 @@ static int getNum(const char **retcp, smallint *retHaveNum, int *retNum) break; default: - *retcp = cp; *retHaveNum = haveNum; *retNum = value; - return TRUE; + return cp; } } } @@ -789,12 +776,13 @@ static void subCommand(const char *cmd, int num1, int num2) */ static void doCommands(void) { - const char *cp; - char *endbuf, buf[USERSIZE]; - int len, num1, num2; - smallint have1, have2; - while (TRUE) { + char buf[USERSIZE]; + const char *cp; + int len; + int n, num1, num2; + smallint h, have1, have2; + /* Returns: * -1 on read errors or EOF, or on bare Ctrl-D. * 0 on ctrl-C, @@ -803,32 +791,36 @@ static void doCommands(void) len = read_line_input(NULL, ": ", buf, sizeof(buf), /*timeout*/ -1); if (len <= 0) return; - endbuf = &buf[len - 1]; - while ((endbuf > buf) && isblank(endbuf[-1])) - endbuf--; - *endbuf = '\0'; - - cp = skip_blank(buf); - have1 = FALSE; - have2 = FALSE; + while (len && isblank(buf[--len])) + buf[len] = '\0'; if ((curNum == 0) && (lastNum > 0)) { curNum = 1; curLine = lines.next; } - if (!getNum(&cp, &have1, &num1)) + have1 = FALSE; + have2 = FALSE; + /* Don't pass &have1, &num1 to getNum() since this forces + * compiler to keep them on stack, not in registers, + * which is usually quite suboptimal. + * Using intermediate variables shrinks code by ~150 bytes. + */ + cp = getNum(skip_whitespace(buf), &h, &n); + if (!cp) continue; + have1 = h; + num1 = n; - cp = skip_blank(cp); - + cp = skip_whitespace(cp); if (*cp == ',') { - cp++; - if (!getNum(&cp, &have2, &num2)) + cp = getNum(cp + 1, &h, &n); + if (!cp) continue; + num2 = n; if (!have1) num1 = 1; - if (!have2) + if (!h) num2 = lastNum; have1 = TRUE; have2 = TRUE; @@ -857,7 +849,7 @@ static void doCommands(void) bb_error_msg("bad file command"); break; } - cp = skip_blank(cp); + cp = skip_whitespace(cp); if (*cp == '\0') { if (fileName) printf("\"%s\"\n", fileName); @@ -874,7 +866,7 @@ static void doCommands(void) break; case 'k': - cp = skip_blank(cp); + cp = skip_whitespace(cp); if ((*cp < 'a') || (*cp > 'z') || cp[1]) { bb_error_msg("bad mark name"); break; @@ -891,7 +883,7 @@ static void doCommands(void) break; case 'q': - cp = skip_blank(cp); + cp = skip_whitespace(cp); if (have1 || *cp) { bb_error_msg("bad quit command"); break; @@ -902,7 +894,7 @@ static void doCommands(void) /* read error/EOF - no way to continue */ if (len < 0) return; - cp = skip_blank(buf); + cp = skip_whitespace(buf); if ((*cp | 0x20) == 'y') /* Y or y */ return; break; @@ -912,7 +904,7 @@ static void doCommands(void) bb_error_msg("bad read command"); break; } - cp = skip_blank(cp); + cp = skip_whitespace(cp); if (*cp == '\0') { bb_error_msg("no file name"); break; @@ -934,7 +926,7 @@ static void doCommands(void) bb_error_msg("bad write command"); break; } - cp = skip_blank(cp); + cp = skip_whitespace(cp); if (!have1) { num1 = 1; num2 = lastNum; -- cgit v1.2.3-55-g6feb From 6bdcee835710e9a7dda6e31a8d5bc3992037db28 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 27 Jul 2017 12:34:56 +0200 Subject: ed: fix "\n" removal in command line; make "w" set "dirty = 0" function old new delta doCommands 2184 2226 +42 getNum 345 343 -2 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 42/-2) Total: 40 bytes Signed-off-by: Denys Vlasenko --- editors/ed.c | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/editors/ed.c b/editors/ed.c index b13c3ed59..7f21ded92 100644 --- a/editors/ed.c +++ b/editors/ed.c @@ -193,9 +193,9 @@ static NOINLINE int searchLines(const char *str, int num1, int num2) /* * Parse a line number argument if it is present. This is a sum - * or difference of numbers, '.', '$', 'x, or a search string. - * Returns pointer which stopped the scan if successful (whether or not - * there was a number). + * or difference of numbers, ".", "$", "'c", or a search string. + * Returns pointer which stopped the scan if successful + * (whether or not there was a number). * Returns NULL if there was a parsing error, with a message output. * Whether there was a number is returned indirectly, as is the number. */ @@ -227,12 +227,13 @@ static const char* getNum(const char *cp, smallint *retHaveNum, int *retNum) case '\'': cp++; - if ((*cp < 'a') || (*cp > 'z')) { + if ((unsigned)(*cp - 'a') >= 26) { bb_error_msg("bad mark name"); return NULL; } haveNum = TRUE; - num = marks[*cp++ - 'a']; + num = marks[(unsigned)(*cp - 'a')]; + cp++; break; case '/': @@ -365,7 +366,7 @@ static void addLines(int num) * Now we exit to ed prompt. Is in important? */ return; } - if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0')) + if (buf[0] == '.' && buf[1] == '\n' && buf[2] == '\0') return; if (!insertLine(num++, buf, len)) return; @@ -791,7 +792,7 @@ static void doCommands(void) len = read_line_input(NULL, ": ", buf, sizeof(buf), /*timeout*/ -1); if (len <= 0) return; - while (len && isblank(buf[--len])) + while (len && isspace(buf[--len])) buf[len] = '\0'; if ((curNum == 0) && (lastNum > 0)) { @@ -801,7 +802,7 @@ static void doCommands(void) have1 = FALSE; have2 = FALSE; - /* Don't pass &have1, &num1 to getNum() since this forces + /* Don't pass &haveN, &numN to getNum() since this forces * compiler to keep them on stack, not in registers, * which is usually quite suboptimal. * Using intermediate variables shrinks code by ~150 bytes. @@ -811,7 +812,6 @@ static void doCommands(void) continue; have1 = h; num1 = n; - cp = skip_whitespace(cp); if (*cp == ',') { cp = getNum(cp + 1, &h, &n); @@ -845,7 +845,7 @@ static void doCommands(void) break; case 'f': - if (*cp && !isblank(*cp)) { + if (*cp != '\0' && *cp != ' ') { bb_error_msg("bad file command"); break; } @@ -862,16 +862,18 @@ static void doCommands(void) break; case 'i': + if (!have1 && lastNum == 0) + num1 = 1; addLines(num1); break; case 'k': cp = skip_whitespace(cp); - if ((*cp < 'a') || (*cp > 'z') || cp[1]) { + if ((unsigned)(*cp - 'a') >= 26 || cp[1]) { bb_error_msg("bad mark name"); break; } - marks[*cp - 'a'] = num2; + marks[(unsigned)(*cp - 'a')] = num2; break; case 'l': @@ -900,7 +902,7 @@ static void doCommands(void) break; case 'r': - if (*cp && !isblank(*cp)) { + if (*cp != '\0' && *cp != ' ') { bb_error_msg("bad read command"); break; } @@ -922,20 +924,22 @@ static void doCommands(void) break; case 'w': - if (*cp && !isblank(*cp)) { + if (*cp != '\0' && *cp != ' ') { bb_error_msg("bad write command"); break; } cp = skip_whitespace(cp); + if (*cp == '\0') { + cp = fileName; + if (!cp) { + bb_error_msg("no file name specified"); + break; + } + } if (!have1) { num1 = 1; num2 = lastNum; - } - if (*cp == '\0') - cp = fileName; - if (cp == NULL) { - bb_error_msg("no file name specified"); - break; + dirty = FALSE; } writeLines(cp, num1, num2); break; -- cgit v1.2.3-55-g6feb From 2e01eec4d3bf52bb3f8332bd74a877a317238f92 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 27 Jul 2017 12:53:20 +0200 Subject: tweak examples/var_service/* Signed-off-by: Denys Vlasenko --- examples/var_service/README | 14 ++++++++------ examples/var_service/README_distro_proposal.txt | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) 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. var_service/dhcp_if -controls a udhcpc instance which provides dhpc-assigned IP +controls a udhcpc instance which provides DHCP-assigned IP address on interface named "if". Copy/rename this directory as needed to run udhcpc on other interfaces (var_service/dhcp_if/run script uses _foo suffix of the parent directory as interface name). @@ -164,9 +164,9 @@ This is achieved very simply by having sv o . at the very beginning of fw/run script, not at the end. -Therefore, any "sv u /var/run/service/fw" command by any other -script "undoes" o(ne-shot) command if fw still runs, thus -runsv will rerun it; or start it in a normal way if fw is not running. +Therefore, any "sv u fw" command by any other script "undoes" o(ne-shot) +command if fw still runs, thus runsv will rerun it; or start it +in a normal way if fw is not running. This mechanism is the reason why fw is a service, not just a script. @@ -198,7 +198,8 @@ PID TIME COMMAND 568 0:00 svlogd -tt /var/log/service/dhcp_eth0 850 0:00 udhcpc -vv --foreground --interface=eth0 --pidfile=/var/service/dhcp_eth0/udhcpc.pid - --script=/var/service/dhcp_eth0/dhcp_handler -x hostname bbox + --script=/var/service/dhcp_eth0/dhcp_handler + -x hostname bbox 563 0:00 runsv ntpd 573 0:01 svlogd -tt /var/log/service/ntpd 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 622 0:00 busybox httpd -p80 -vvv -f -h /home/httpd_root 577 0:00 runsv supplicant_wlan0 627 0:00 svlogd -tt /var/log/service/supplicant_wlan0 -638 0:03 wpa_supplicant -i wlan0 -c /var/service/supplicant_wlan0/wpa_supplicant.conf -d +638 0:03 wpa_supplicant -i wlan0 + -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: (busybox has it included) - s6: by Laurent Bercot, http://skarnet.org/software/s6/ - It is not required that a specific clone should be used. Let evolution work. + Terminology daemon: any long running background program. Common examples are sshd, getty, -- cgit v1.2.3-55-g6feb From 1d8df52d451c2085365dd2a457e498a7891348ee Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 27 Jul 2017 13:34:51 +0200 Subject: inetd: improve --helpt text and config help text. Signed-off-by: Denys Vlasenko --- networking/httpd.c | 4 ++-- networking/inetd.c | 39 +++++++++++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 12 deletions(-) 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 @@ //config: different user. //config: //config:config FEATURE_HTTPD_BASIC_AUTH -//config: bool "Enable Basic http Authentication" +//config: bool "Enable HTTP authentication" //config: default y //config: depends on HTTPD //config: help @@ -134,7 +134,7 @@ //config: /adm:toor:PaSsWd //config: //config:config FEATURE_HTTPD_AUTH_MD5 -//config: bool "Support MD5 crypted passwords for http Authentication" +//config: bool "Support MD5-encrypted passwords in HTTP authentication" //config: default y //config: depends on FEATURE_HTTPD_BASIC_AUTH //config: help 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 @@ //config: Internet superserver daemon //config: //config:config FEATURE_INETD_SUPPORT_BUILTIN_ECHO -//config: bool "Support echo service" +//config: bool "Support echo service on port 7" //config: default y //config: depends on INETD //config: help -//config: Echo received data internal inetd service +//config: Internal service which echoes data back. +//config: Activated by configuration lines like these: +//config: echo stream tcp nowait root internal +//config: echo dgram udp wait root internal //config: //config:config FEATURE_INETD_SUPPORT_BUILTIN_DISCARD -//config: bool "Support discard service" +//config: bool "Support discard service on port 8" //config: default y //config: depends on INETD //config: help -//config: Internet /dev/null internal inetd service +//config: Internal service which discards all input. +//config: Activated by configuration lines like these: +//config: discard stream tcp nowait root internal +//config: discard dgram udp wait root internal //config: //config:config FEATURE_INETD_SUPPORT_BUILTIN_TIME -//config: bool "Support time service" +//config: bool "Support time service on port 37" //config: default y //config: depends on INETD //config: help -//config: Return 32 bit time since 1900 internal inetd service +//config: Internal service which returns big-endian 32-bit number +//config: of seconds passed since 1900-01-01. The number wraps around +//config: on overflow. +//config: Activated by configuration lines like these: +//config: time stream tcp nowait root internal +//config: time dgram udp wait root internal //config: //config:config FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME -//config: bool "Support daytime service" +//config: bool "Support daytime service on port 13" //config: default y //config: depends on INETD //config: help -//config: Return human-readable time internal inetd service +//config: Internal service which returns human-readable time. +//config: Activated by configuration lines like these: +//config: daytime stream tcp nowait root internal +//config: daytime dgram udp wait root internal //config: //config:config FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN -//config: bool "Support chargen service" +//config: bool "Support chargen service on port 19" //config: default y //config: depends on INETD //config: help -//config: Familiar character generator internal inetd service +//config: Internal service which generates endless stream +//config: of all ASCII chars beetween space and char 126. +//config: Activated by configuration lines like these: +//config: chargen stream tcp nowait root internal +//config: chargen dgram udp wait root internal //config: //config:config FEATURE_INETD_RPC //config: bool "Support RPC services" @@ -216,6 +234,7 @@ //usage: "\n -q N Socket listen queue (default 128)" //usage: "\n -R N Pause services after N connects/min" //usage: "\n (default 0 - disabled)" +//usage: "\n Default CONFFILE is /etc/inetd.conf" #include #include /* setrlimit */ -- cgit v1.2.3-55-g6feb From e9a5a6985ca7f484df60df1f9bdc8b62eac88f2e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 27 Jul 2017 14:31:59 +0200 Subject: rdate: tweak comments, no code changes Signed-off-by: Denys Vlasenko --- util-linux/rdate.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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) uint32_t nett; int fd; - /* Add a timeout for dead or inaccessible servers */ + /* Timeout for dead or inaccessible servers */ alarm(10); signal(SIGALRM, socket_timeout); @@ -53,9 +53,8 @@ static time_t askremotedate(const char *host) close(fd); /* Convert from network byte order to local byte order. - * RFC 868 time is the number of seconds - * since 00:00 (midnight) 1 January 1900 GMT - * the RFC 868 time 2,208,988,800 corresponds to 00:00 1 Jan 1970 GMT + * RFC 868 time is seconds since 1900-01-01 00:00 GMT. + * RFC 868 time 2,208,988,800 corresponds to 1970-01-01 00:00 GMT. * Subtract the RFC 868 time to get Linux epoch. */ nett = ntohl(nett) - RFC_868_BIAS; @@ -66,7 +65,7 @@ static time_t askremotedate(const char *host) * current time cur = 0x123ffffffff. * Assuming our time is not some 40 years off, * remote time must be 0x12400000001. - * Need to adjust out time by (int32_t)(nett - cur). + * Need to adjust our time by (int32_t)(nett - cur). */ time_t cur = time(NULL); int32_t adjust = (int32_t)(nett - (uint32_t)cur); -- cgit v1.2.3-55-g6feb From 5b3cbe3a535db27302d979bddefa28ca492647e9 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 27 Jul 2017 14:45:25 +0200 Subject: config: more tweaking of help texts Signed-off-by: Denys Vlasenko --- miscutils/crond.c | 2 +- util-linux/getopt.c | 2 +- util-linux/mount.c | 6 +++--- util-linux/umount.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) 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 @@ //config: 40 4 * * * /etc/cron/daily > /dev/null 2>&1 //config: //config:config FEATURE_CROND_D -//config: bool "Support option -d to redirect output to stderr" +//config: bool "Support -d (redirect output to stderr)" //config: depends on CROND //config: default y //config: help 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 @@ //config: wisely leave this disabled. //config: //config:config FEATURE_GETOPT_LONG -//config: bool "Support option -l" +//config: bool "Support -l LONGOPTs" //config: default y if LONG_OPTS //config: depends on GETOPT //config: help diff --git a/util-linux/mount.c b/util-linux/mount.c index 5f030607c..4d5c2243a 100644 --- a/util-linux/mount.c +++ b/util-linux/mount.c @@ -29,14 +29,14 @@ //config: NFS filesystems. //config: //config:config FEATURE_MOUNT_FAKE -//config: bool "Support option -f" +//config: bool "Support -f (fake mount)" //config: default y //config: depends on MOUNT //config: help //config: Enable support for faking a file system mount. //config: //config:config FEATURE_MOUNT_VERBOSE -//config: bool "Support option -v" +//config: bool "Support -v (verbose)" //config: default y //config: depends on MOUNT //config: help @@ -98,7 +98,7 @@ //config: //config:config FEATURE_MOUNT_FSTAB //config: depends on MOUNT -//config: bool "Support /etc/fstab and -a" +//config: bool "Support /etc/fstab and -a (mount all)" //config: default y //config: help //config: Support mount all and looking for files in /etc/fstab. 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 @@ //config: utility, you almost certainly also want to enable 'umount'. //config: //config:config FEATURE_UMOUNT_ALL -//config: bool "Support option -a" +//config: bool "Support -a (unmount all)" //config: default y //config: depends on UMOUNT //config: help -- cgit v1.2.3-55-g6feb From 46f3f16b587ce781ab43a7b17698e1e565b2acf7 Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Tue, 25 Jul 2017 20:44:50 +0200 Subject: unzip: fix regression on big-endian machines This fixes a regression which was introduced with commit 2a0867a5 ("unzip: optional support for bzip2 and lzma") and causes unzip to exit with error when extracting archives: unzip: unsupported method 2048 Signed-off-by: Natanael Copa Signed-off-by: Denys Vlasenko --- archival/unzip.c | 1 + 1 file changed, 1 insertion(+) diff --git a/archival/unzip.c b/archival/unzip.c index bb39d954e..8ed9ae7d5 100644 --- a/archival/unzip.c +++ b/archival/unzip.c @@ -114,6 +114,7 @@ typedef union { #define FIX_ENDIANNESS_ZIP(zip) \ do { if (BB_BIG_ENDIAN) { \ + (zip).fmt.method = SWAP_LE16((zip).fmt.method ); \ (zip).fmt.crc32 = SWAP_LE32((zip).fmt.crc32 ); \ (zip).fmt.cmpsize = SWAP_LE32((zip).fmt.cmpsize ); \ (zip).fmt.ucmpsize = SWAP_LE32((zip).fmt.ucmpsize ); \ -- cgit v1.2.3-55-g6feb From 619d9b5e6848a72350126ea9c1e413fd133181e3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 28 Jul 2017 15:28:33 +0200 Subject: ash: less hackish implementation of evaltreenr() Defining a function alias with __attribute__ ((alias("evaltree"),__noreturn__)) is not that usual, and clang had a bug which made it misunderstand this construct. Switch to: ALWAYS_INLINE NORETURN evaltreenr() { evaltree(); unreachable(); } Older gcc's do not know unreachable(), on them we pay the price of having a few extra calls to abort(): function old new delta evalsubshell 151 156 +5 evalpipe 357 362 +5 argstr 1141 1144 +3 On newer gcc, code size does not change. Signed-off-by: Denys Vlasenko --- include/platform.h | 7 +++++++ shell/ash.c | 20 ++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/include/platform.h b/include/platform.h index 8210e5c49..ea49c7e92 100644 --- a/include/platform.h +++ b/include/platform.h @@ -45,6 +45,13 @@ #define UNUSED_PARAM __attribute__ ((__unused__)) #define NORETURN __attribute__ ((__noreturn__)) + +#if __GNUC_PREREQ(4,5) +# define bb_unreachable(altcode) __builtin_unreachable() +#else +# define bb_unreachable(altcode) altcode +#endif + /* "The malloc attribute is used to tell the compiler that a function * may be treated as if any non-NULL pointer it returns cannot alias * any other pointer valid when the function returns. This will often diff --git a/shell/ash.c b/shell/ash.c index f74fbd72f..1f5a8dae0 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -6137,6 +6137,19 @@ struct backcmd { /* result of evalbackcmd */ #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ static int evaltree(union node *, int); +/* An evaltree() which is known to never return. + * Used to use an alias: + * static int evaltreenr(union node *, int) __attribute__((alias("evaltree"),__noreturn__)); + * but clang was reported to "transfer" noreturn-ness to evaltree() as well. + */ +static ALWAYS_INLINE NORETURN void +evaltreenr(union node *n, int flags) +{ + evaltree(n, flags); + bb_unreachable(abort()); + /* NOTREACHED */ +} + static void FAST_FUNC evalbackcmd(union node *n, struct backcmd *result) { @@ -6173,7 +6186,7 @@ evalbackcmd(union node *n, struct backcmd *result) */ eflag = 0; ifsfree(); - evaltree(n, EV_EXIT); /* actually evaltreenr... */ + evaltreenr(n, EV_EXIT); /* NOTREACHED */ } /* parent */ @@ -8796,11 +8809,6 @@ evaltree(union node *n, int flags) return exitstatus; } -#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) -static -#endif -int evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__)); - static int skiploop(void) { -- cgit v1.2.3-55-g6feb From be366e5afac1d9f5b3958bd3899a389308d5d9d3 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Thu, 27 Jul 2017 13:53:39 +0100 Subject: ash: support platforms that don't have '%m' printf specifier The '%m' conversion specifier prints an error message based on the current value of 'errno'. It is available in the GNU C library, Cygwin (since 2012), uClibc and musl. It is not available in various BSDs, BSD-derived systems (MacOS, Android) or Microsoft Windows. Use a symbol defined in platform.h to control how error messages can be formatted to display the 'errno' message. On platforms that support it use '%m'; on other platforms use '%s' and strerror(). On platforms that have '%m' there is essentially no change in the size of the binary. Otherwise: function old new delta redirect 1287 1310 +23 xtcsetpgrp 27 44 +17 dup2_or_raise 34 51 +17 setinputfile 267 275 +8 .rodata 163379 163371 -8 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 4/1 up/down: 65/-8) Total: 57 bytes Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- include/platform.h | 4 ++++ shell/ash.c | 22 +++++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/include/platform.h b/include/platform.h index ea49c7e92..b81c59d4e 100644 --- a/include/platform.h +++ b/include/platform.h @@ -406,6 +406,7 @@ typedef unsigned smalluint; #define HAVE_MNTENT_H 1 #define HAVE_NET_ETHERNET_H 1 #define HAVE_SYS_STATFS_H 1 +#define HAVE_PRINTF_PERCENTM 1 #if defined(__UCLIBC__) # if UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32) @@ -461,6 +462,7 @@ typedef unsigned smalluint; # undef HAVE_DPRINTF # undef HAVE_UNLOCKED_STDIO # undef HAVE_UNLOCKED_LINE_OPS +# undef HAVE_PRINTF_PERCENTM #endif #if defined(__dietlibc__) @@ -483,6 +485,7 @@ typedef unsigned smalluint; # undef HAVE_STRVERSCMP # undef HAVE_XTABS # undef HAVE_UNLOCKED_LINE_OPS +# undef HAVE_PRINTF_PERCENTM # include # if __FreeBSD_version < 1000029 # undef HAVE_STRCHRNUL /* FreeBSD added strchrnul() between 1000028 and 1000029 */ @@ -517,6 +520,7 @@ typedef unsigned smalluint; # undef HAVE_STRVERSCMP # undef HAVE_UNLOCKED_LINE_OPS # undef HAVE_NET_ETHERNET_H +# undef HAVE_PRINTF_PERCENTM #endif /* diff --git a/shell/ash.c b/shell/ash.c index 1f5a8dae0..f9c78ee78 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -1306,6 +1306,18 @@ ash_msg_and_raise_error(const char *msg, ...) va_end(ap); } +/* + * Use '%m' to append error string on platforms that support it, '%s' and + * strerror() on those that don't. + * + * 'fmt' must be a string literal. + */ +#ifdef HAVE_PRINTF_PERCENTM +#define ash_msg_and_raise_perror(fmt, ...) ash_msg_and_raise_error(fmt ": %m", ##__VA_ARGS__) +#else +#define ash_msg_and_raise_perror(fmt, ...) ash_msg_and_raise_error(fmt ": %s", ##__VA_ARGS__, strerror(errno)) +#endif + static void raise_error_syntax(const char *) NORETURN; static void raise_error_syntax(const char *msg) @@ -3827,7 +3839,7 @@ static void xtcsetpgrp(int fd, pid_t pgrp) { if (tcsetpgrp(fd, pgrp)) - ash_msg_and_raise_error("can't set tty process group (%m)"); + ash_msg_and_raise_perror("can't set tty process group"); } /* @@ -5313,7 +5325,7 @@ savefd(int from) err = newfd < 0 ? errno : 0; if (err != EBADF) { if (err) - ash_msg_and_raise_error("%d: %m", from); + ash_msg_and_raise_perror("%d", from); close(from); fcntl(newfd, F_SETFD, FD_CLOEXEC); } @@ -5328,7 +5340,7 @@ dup2_or_raise(int from, int to) newfd = (from != to) ? dup2(from, to) : to; if (newfd < 0) { /* Happens when source fd is not open: try "echo >&99" */ - ash_msg_and_raise_error("%d: %m", from); + ash_msg_and_raise_perror("%d", from); } return newfd; } @@ -5459,7 +5471,7 @@ redirect(union node *redir, int flags) /* "echo >&10" and 10 is a fd opened to a sh script? */ if (is_hidden_fd(sv, right_fd)) { errno = EBADF; /* as if it is closed */ - ash_msg_and_raise_error("%d: %m", right_fd); + ash_msg_and_raise_perror("%d", right_fd); } newfd = -1; } else { @@ -5493,7 +5505,7 @@ redirect(union node *redir, int flags) if (newfd >= 0) close(newfd); errno = i; - ash_msg_and_raise_error("%d: %m", fd); + ash_msg_and_raise_perror("%d", fd); /* NOTREACHED */ } /* EBADF: it is not open - good, remember to close it */ -- cgit v1.2.3-55-g6feb From f128bdbbd7ab14146a45166d20348f7705888353 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 29 Jul 2017 00:59:24 +0200 Subject: shell: make standalone shell tab-complete "busybox" function old new delta busybox_main - 624 +624 packed_usage 31758 31777 +19 applet_names 2638 2646 +8 applet_main 1528 1532 +4 applet_install_loc 191 192 +1 run_applet_and_exit 681 78 -603 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 4/1 up/down: 656/-603) Total: 53 bytes Signed-off-by: Denys Vlasenko --- libbb/appletlib.c | 23 +++++++++++++++++++---- libbb/lineedit.c | 4 ++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/libbb/appletlib.c b/libbb/appletlib.c index b9fbbd1f2..7a1a7f005 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c @@ -760,11 +760,26 @@ static void install_links(const char *busybox UNUSED_PARAM, } # endif -# if ENABLE_BUSYBOX static void run_applet_and_exit(const char *name, char **argv) NORETURN; -/* If we were called as "busybox..." */ -static int busybox_main(char **argv) +# if ENABLE_BUSYBOX +# if ENABLE_FEATURE_SH_STANDALONE && ENABLE_FEATURE_TAB_COMPLETION + /* + * Insert "busybox" into applet table as well. + * This makes standalone shell tab-complete this name too. + * (Otherwise having "busybox" in applet table is not necessary, + * there is other code which routes "busyboxANY_SUFFIX" name + * to busybox_main()). + */ +//usage:#define busybox_trivial_usage NOUSAGE_STR +//usage:#define busybox_full_usage "" +//applet:IF_BUSYBOX(IF_FEATURE_SH_STANDALONE(IF_FEATURE_TAB_COMPLETION(APPLET(busybox, BB_DIR_BIN, BB_SUID_MAYBE)))) +int busybox_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; +# else +# define busybox_main(argc,argv) busybox_main(argv) +static +# endif +int busybox_main(int argc UNUSED_PARAM, char **argv) { if (!argv[1]) { /* Called without arguments */ @@ -937,7 +952,7 @@ static NORETURN void run_applet_and_exit(const char *name, char **argv) { # if ENABLE_BUSYBOX if (is_prefixed_with(name, "busybox")) - exit(busybox_main(argv)); + exit(busybox_main(/*unused:*/ 0, argv)); # endif # if NUM_APPLETS > 0 /* find_applet_by_name() search is more expensive, so goes second */ diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 2a5d4e704..e5721b063 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -776,7 +776,7 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type) } pf_len = strlen(pfind); -#if ENABLE_FEATURE_SH_STANDALONE && NUM_APPLETS != 1 +# if ENABLE_FEATURE_SH_STANDALONE && NUM_APPLETS != 1 if (type == FIND_EXE_ONLY && !dirbuf) { const char *p = applet_names; @@ -787,7 +787,7 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type) continue; } } -#endif +# endif for (i = 0; i < npaths; i++) { DIR *dir; -- cgit v1.2.3-55-g6feb From 00a1dbd230a3e0ee2cea84130b7f20d9c9a9cf4e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 29 Jul 2017 01:20:53 +0200 Subject: ash: make tryexec(cmd) parameter const char Fewer casts this way. Signed-off-by: Denys Vlasenko --- shell/ash.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index f9c78ee78..0de81b325 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -7687,7 +7687,7 @@ static int builtinloc = -1; /* index in path of %builtin, or -1 */ static void -tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp) +tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp) { #if ENABLE_FEATURE_SH_STANDALONE if (applet_no >= 0) { @@ -7713,7 +7713,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char ** #else execve(cmd, argv, envp); #endif - if (cmd != (char*) bb_busybox_exec_path && errno == ENOEXEC) { + if (cmd != bb_busybox_exec_path && errno == ENOEXEC) { /* Run "cmd" as a shell script: * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html * "If the execve() function fails with ENOEXEC, the shell @@ -7730,8 +7730,8 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char ** * message and exit code 126. For one, this prevents attempts * to interpret foreign ELF binaries as shell scripts. */ - argv[0] = cmd; - cmd = (char*) bb_busybox_exec_path; + argv[0] = (char*) cmd; + cmd = bb_busybox_exec_path; /* NB: this is only possible because all callers of shellexec() * ensure that the argv[-1] slot exists! */ -- cgit v1.2.3-55-g6feb From 147d2ce3dd98945a328ab9390dc562923b17c2f3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 29 Jul 2017 02:19:01 +0200 Subject: standalone shell / prefer_applets: fix "exe" in comm fields function old new delta main 92 106 +14 Signed-off-by: Denys Vlasenko --- libbb/appletlib.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 7a1a7f005..fa28d433b 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c @@ -34,6 +34,13 @@ # include /* for mallopt */ #endif +#include +#ifndef PR_SET_NAME +#define PR_SET_NAME 15 +#endif +#ifndef PR_GET_NAME +#define PR_GET_NAME 16 +#endif /* Declare _main() */ #define PROTOTYPES @@ -1056,6 +1063,17 @@ int main(int argc UNUSED_PARAM, char **argv) if (applet_name[0] == '-') applet_name++; applet_name = bb_basename(applet_name); + +# if defined(__linux__) + /* If we are a result of execv("/proc/self/exe"), fix ugly comm of "exe" */ + if (ENABLE_FEATURE_SH_STANDALONE + || ENABLE_FEATURE_PREFER_APPLETS + || !BB_MMU + ) { + prctl(PR_SET_NAME, (long)applet_name, 0, 0, 0); + } +# endif + parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */ run_applet_and_exit(applet_name, argv); -- cgit v1.2.3-55-g6feb From 5d725fd627e23b4be5d5a8b843a5a9fc9bc9e410 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 29 Jul 2017 02:20:17 +0200 Subject: ps: implement -o sid function old new delta func_sid - 29 +29 out_spec 320 340 +20 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 1/0 up/down: 49/0) Total: 49 bytes Signed-off-by: Denys Vlasenko --- procps/ps.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/procps/ps.c b/procps/ps.c index d4d787d78..6348df5f2 100644 --- a/procps/ps.c +++ b/procps/ps.c @@ -340,6 +340,11 @@ static void func_pgid(char *buf, int size, const procps_status_t *ps) sprintf(buf, "%*u", size, ps->pgid); } +static void func_sid(char *buf, int size, const procps_status_t *ps) +{ + sprintf(buf, "%*u", size, ps->sid); +} + static void put_lu(char *buf, int size, unsigned long u) { char buf4[5]; @@ -458,6 +463,7 @@ static const ps_out_t out_spec[] = { { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY }, { 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ }, /* Not mandated, but useful: */ + { 5 , "sid" ,"SID" ,func_sid ,PSSCAN_SID }, { 4 , "stat" ,"STAT" ,func_state ,PSSCAN_STATE }, { 4 , "rss" ,"RSS" ,func_rss ,PSSCAN_RSS }, #if ENABLE_SELINUX -- cgit v1.2.3-55-g6feb From 69be994de69d794f038f10a3e7a67519b2006581 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 29 Jul 2017 02:27:12 +0200 Subject: ps: tweak outdated comment, no code changes Signed-off-by: Denys Vlasenko --- procps/ps.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/procps/ps.c b/procps/ps.c index 6348df5f2..eb1946d27 100644 --- a/procps/ps.c +++ b/procps/ps.c @@ -429,11 +429,6 @@ static void func_label(char *buf, int size, const procps_status_t *ps) #endif /* -static void func_nice(char *buf, int size, const procps_status_t *ps) -{ - ps->??? -} - static void func_pcpu(char *buf, int size, const procps_status_t *ps) { } -- cgit v1.2.3-55-g6feb